Can one use template / preprocessor hacking to support variable arguments in the middle of the argument list?

I came across an old code that looks like this:

void dothing(bool testBool, const std::string& testString1, const std::string& file, int line, const std::string& defaultString = "") { // do something... } void dothings(bool testBool, const std::string& testString1, const std::string& testString2, const std::string& file, int line, const std::string& defaultString = "") { dothing(testBool, testString1, file, line, defaultString); dothing(testBool, testString2, file, line, defaultString); } void dothings(bool testBool, const std::string& testString1, const std::string& testString2, const std::string& testString3, const std::string& file, int line, const std::string& defaultString = "") { dothings(testBool, testString1, testString2, file, line, defaultString); dothing(testBool, testString3, file, line, defaultString); } void dothings(bool testBool, const std::string& testString1, const std::string& testString2, const std::string& testString3, const std::string& testString4, const std::string& file, int line, const std::string& defaultString = "") { dothings(testBool, testString1, testString2, testString3, file, line, defaultString); dothing(testBool, testString1, file, line, defaultString); } 

This is ridiculous, and I'm trying to reorganize it:

  void dothings(bool testBool, std::initializer_list<std::string> testStrings, const std::string& file, int line, const std::string& defaultString = "") { for(auto iter = testStrings.begin(); iter != testStrings.end(); ++iter) { dothing(testBool, *iter, file, line, defaultString); } } 

The problem is that these functions are used a lot, and I would like to write a macro or template in such a way that all previous functions build a list of string initializations of all test lines and pass them one new function. I want to write something like this:

 #define dothings(testBool, (args), file, line) dothings(testBool, {args}, file, line) 

I really don't need the default string in these functions, but if there is a way to support it, that would be great.

I have access to the C ++ 11 compiler and ONLY SUPPORT.

I cannot reorder the arguments of these functions.

I saw some interesting posts about variable argument macros, but just didn't press how to apply them to this case.

+7
c ++ macros c ++ 11 templates
source share
3 answers

This is just one of the possible solutions, it can be improved to determine if there is an extra line by default at the end or not (using some other metaprogramming method along with SFINAE). This uses the index trick to split the argument list into two subsequences: one for the three trailing parameters and one for the lines themselves. In the end, each line is paired with the rest of the arguments, and the dothing function calls are extended .

 void dothing(bool testBool , const std::string& str , const std::string& file , int line , const std::string& defaultString) { // processing of a single str } template <typename... Args, std::size_t... Is> void dothings(bool testBool, std::index_sequence<Is...>, Args&&... args) { auto tuple = std::make_tuple(std::forward<Args>(args)...); using expander = int[]; static_cast<void>(expander{ 0, (dothing(testBool, std::get<Is>(tuple) , std::get<sizeof...(Args)-3>(tuple) , std::get<sizeof...(Args)-2>(tuple) , std::get<sizeof...(Args)-1>(tuple)), 0)... }); } template <typename... Args> void dothings(bool testBool, Args&&... args) { dothings(testBool , std::make_index_sequence<sizeof...(Args)-3>{} , std::forward<Args>(args)...); } 

Demo

+6
source share

Assuming that you really cannot change the order of the argument, you can use two functions of the variational pattern:

 int lastArgument(int testInt) { return testInt; } template<typename T, typename... Arguments> int lastArgument(T t, Arguments... parameters) { return lastArgument(parameters...); } void someFunction(bool testBool, const string& test, int testInt) { //do something } template<typename T, typename... Arguments> void someFunction(bool testBool, const T& test, Arguments... parameters) { int testInt = lastArgument(parameters...); someFunction(testBool, test, testInt); someFunction(testBool, parameters...); } 

You can do something similar to get the last two options

0
source share

Here is what I mentioned earlier. Instead of jumping through template hoops, you can simply write a regular variational function that is called from existing static argument functions. Lingua franca usually does not support variable arguments in the middle of argument lists, and it seems like it might be a bit of a bad idea to encourage it.

A variant function may look like this:

 template <typename T, typename = typename std::enable_if<std::is_same<T, std::string>::value>::type> void dothings(bool testBool, const std::string& file, int line, const std::string& defaultString, T t) { dothing(testBool, t, file,line, defaultString); } template <typename T, typename... Ts> void dothings(bool testBool, const std::string& file, int line, const std::string& defaultString, T t, Ts... ts) { dothing(testBool, file,line, defaultString, t); dothing(testBool, file,line, defaultString, ts...); } 

Then just call the variable function from the static functions of the arguments ...

 void dothings(bool testBool, const std::string& testString1, const std::string& testString2, const std::string& file, int line, const std::string& defaultString) { dothing(testBool, file, line, defaultString testString1, testString2); } 

etc...

This preserves compatibility, allows the use of various options, and even encourages people to use them if they need more than n test lines. This is a little more verbose, but perhaps more people will be able to support it.

Note: std::array can eliminate a recursive pattern

0
source share

All Articles