user6245072 user6245072 - 1 month ago 12
C++ Question

Simplifying work with variadic functions / macros

I'm writing a bunch of functions like this:

template <>
Error SyntaxError<unfinished_string>(unsigned int line) {
std::stringstream message;
message << "SyntaxError: unfinished string (starting at line " << line << ").";
return Error(Error::Type::syntax, message.str());
}

template <>
Error SyntaxError<malformed_number>(std::string number, unsigned int line) {
std::stringstream message;
message << "SyntaxError: malformed number (" << number << ") at line " << line << '.';
return Error(Error::Type::Syntax, message.str());
}

...


And it wouldn't be bad to have a variadic function / macro that looked something like this:

Error proto(/*variable number & type of arguments*/) {
std::stringstream message;
message << "SyntaxError: " << /*arguments passed, separated by <<s */;
return Error(Error::Type::syntax, message.str());
}


So that I could then write my functions as:

template <>
Error SyntaxError<unfinished_string>(unsigned int line) {
return proto("unfinished string (starting at line ", line, ").");
}

template <>
Error SyntaxError<malformed_number>(std::string number, unsigned int line) {
return proto("malformed number (", number, ") at line ", line, '.');
}


Is that by any chance possible? How if yes?

Answer

If you want to convert a variadic list of arguments and stream them altogether, then you could implement an additional function that recursively appends each argument to that stream. Example:

template< typename FirstArg, typename... Args >
inline std::ostream& join_args( std::ostream& os, FirstArg arg, Args... args ) {
   os << arg;
   return join_args( os, args... );
}

Step by step, you will be exhausting the arguments. Take care of the order you process it, ( std::ostream& os, FirstArg arg, Args... args ) is not the same as ( std::ostream& os, Args... args, LastArg arg ).

Last but not least, you should cover the corner cases (either one or no arguments, depending on your problem):

inline std::ostream& join_args( std::ostream& os ) {
   return os;
}

Don't worry about recursion efficiency, the compiler will be able to inline all the function calls (as it knows at compile time how deep the recursion is) and flatten all the code, so that the resulting program would be more or less equivalent to manually appending all the arguments into a single stream.

Comments