模板/预处理器 hackery 可以用于支持参数列表中间的变量参数吗?
Can Template / Preprocessor hackery be used to support variable arguments in the middle of an argument list?
我偶然发现了如下所示的旧代码:
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 编译器和 boost。
我无法对这些函数的参数重新排序。
我看过一些关于可变参数宏的有趣帖子,但只是没有点击如何将它们应用到这种情况。
这只是其中一种可能的解决方案,可以改进它来检测末尾是否有额外的默认字符串(通过一些其他的元编程技术和 SFINAE)。这个利用了 to split the arguments list into two subsequences: one for the three trailing parameters, and one for the strings themselves. Eventually, each string is paired with the remaining arguments and calls to function dothing
are 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)...);
}
假设你真的不能改变参数的顺序,你可以使用两个可变参数模板函数:
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...);
}
您可以执行类似的操作来检索最后两个参数
这是我之前提到的。您可以编写一个从现有静态参数函数调用的普通可变参数函数,而不是跳过模板圈。通用语通常不支持参数列表中间的可变参数,鼓励它似乎是个坏主意。
可变参数函数可能如下所示...
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);
}
等...
这保留了兼容性,允许 properish 可变使用,甚至鼓励人们在需要超过 n 个测试字符串时使用它。它稍微冗长一些,但可以说更多的人能够维护它。
注意:std::array
可以去掉递归模板
我偶然发现了如下所示的旧代码:
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 编译器和 boost。
我无法对这些函数的参数重新排序。
我看过一些关于可变参数宏的有趣帖子,但只是没有点击如何将它们应用到这种情况。
这只是其中一种可能的解决方案,可以改进它来检测末尾是否有额外的默认字符串(通过一些其他的元编程技术和 SFINAE)。这个利用了 dothing
are 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)...);
}
假设你真的不能改变参数的顺序,你可以使用两个可变参数模板函数:
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...);
}
您可以执行类似的操作来检索最后两个参数
这是我之前提到的。您可以编写一个从现有静态参数函数调用的普通可变参数函数,而不是跳过模板圈。通用语通常不支持参数列表中间的可变参数,鼓励它似乎是个坏主意。
可变参数函数可能如下所示...
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);
}
等...
这保留了兼容性,允许 properish 可变使用,甚至鼓励人们在需要超过 n 个测试字符串时使用它。它稍微冗长一些,但可以说更多的人能够维护它。
注意:std::array
可以去掉递归模板