I'm trying to write some sort of command handler, which can tokenize an istringstream, automatically convert the tokens into variables of specific types and call a callback function with the converted variables as arguments. Here is simplified version of my code:
void Callback(int x, char y, float z) {
// do whatever
// note: For simplicity, I use a callback with a fixed signature
// here. In my actual implementation, the callback can be
// called with any number and types of arguments - but that
// I have solved already.
}
template<typename T>
T GetNextArgument(std::istringstream& strm) {
// get one token from the input stream and convert it to the required type
T val;
strm >> val;
return val;
}
template<typename ...Args>
void ParseAndExecute(std::istringstream& input_stream) {
Callback(GetNextArgument<Args>(input_stream)...);
}
int main() {
std::istringstream strm("15 a 17.3");
ParseAndExecute(strm);
return 0;
}
The problem I have is that the ParseAndExecute()
function after parameter pack expansion looks like this:
void ParseAndExecute(std::istringstream& strm) {
Callback(GetNextArgument<int>(strm),
GetNextArgument<char>(strm),
GetNextArgument<float>(strm));
}
Since the order of evaluation of the arguments is not defined, the tokens may be taken from the stream in incorrect order (and in my case, they always are). Instead I would need the expansion to give me something more like that:
void ParseAndExecute(std::istringstream& strm) {
int a1 = GetNextArgument<int>(strm);
char a2 = GetNextArgument<char>(strm);
float a3 = GetNextArgument<float>(strm);
Callback(a1, a2, a3);
}
But I cannot see how to achieve that with parameter pack expansion. Maybe with a recursive template...? Or do you have any other suggestion to achieve a similar functionality?
You could use an intermediate std::tuple
with list initialization because left-to-right order is mandatory in this case:
std::tuple<Args...> tuple_args = {GetNextArgument<Args>(input_stream)... };
std::apply([](auto&&... args) {
Callback(std::forward<decltype(args)>(args)... );
}, std::move(tuple_args));
You have to use a lambda if Callback
does not have a fixed signature as you mentioned and you want to rely on deduction.
struct Caller {
template<class...Args>
Caller(Args&&... args) { Callback(std::forward<Args>(args)...); }
};
template<typename ...Args>
void ParseAndExecute(std::istringstream& input_stream) {
Caller{GetNextArgument<Args>(input_stream)...};
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.