可以使用模板/预处理器hackery来支持参数列表中间的变量参数吗?

时间:2015-08-03 18:41:17

标签: c++ templates c++11 macros

我偶然发现了看起来像这样的旧代码:

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);
}

这很荒谬,我试图重构它:

 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);
    }
}

问题是这些函数被大量使用了,我想编写一个宏或模板,以便所有以前的函数构造所有测试字符串的字符串的初始化列表并将它们传递给一个新功能。我想写这样的东西:

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

我并不关心这些函数中的默认字符串,但如果有办法支持它,那就太棒了。

我可以访问c ++ 11编译器并仅提升。

我无法对这些函数的参数重新排序。

我看过一些关于变量参数宏的有趣帖子,但它并没有点击如何将它们应用于这种情况。

3 个答案:

答案 0 :(得分:6)

这只是可能的解决方案之一,可以改进以检测最后是否存在额外的默认字符串(通过与SFINAE一起使用的其他元编程技术)。这个利用indices trick将参数列表拆分为两个子序列:一个用于三个尾随参数,另一个用于字符串本身。最终,每个字符串与其余参数配对,对函数dothing的调用为expanded

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

答案 1 :(得分:0)

假设你真的无法改变参数的顺序,你可以使用两个可变参数模板函数:

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...);
}

你可以做类似的事情来检索最后两个参数

答案 2 :(得分:0)

这是我之前提到的。您可以编写一个从现有静态参数函数调用的正常可变参数函数,而不是跳过模板箍。通用语言通常不支持参数列表中间的变量参数,鼓励它似乎有点不好。

可变函数可能如下所示......

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...);

}

然后从静态参数函数中调用变量函数...

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);
}

等...

这保留了兼容性,允许正确的可变参数使用,甚至鼓励人们在需要 n 测试字符串时使用它。它略显冗长,但可以说更多的人能够维持它。

注意:std::array可以消除递归模板