接受字符串和整数的可变参数函数,格式化后者并连接所有?
A variadic function that accepts Strings and Ints, Format the latter and concatenate all?
我正在尝试在这个 question 中使用 DanielKO 的答案来满足我的需要,但我不熟悉模板和可变参数函数,我不知道我该怎么做。
我需要的是一个可变的 c++(11) 函数,我可以这样调用它:
String NewMsg = CreateMessage("SET",16,1,17,0,"RED",47);
并有 NewMsg= "SET,0010,0001,0011,0000,RED,002F".
我什至不知道应该在参数之间添加逗号的位置。
接着:
我如何在解析 args 时区分整数和字符串,以便将每个整数格式化为十六进制字符串?
你使用递归和函数重载
std::string CreateMessage(int i)
{
return /* i formatted as hex */;
}
std::string CreateMessage(const char* s)
{
return s;
}
template<typename T, typename... Ts>
std::string CreateMessage(T t, Ts... ts)
{
return CreateMessage(t) + "," + CreateMessage(ts...);
}
一种选择是对模板使用递归,正如 Passer By 在他的回答中所做的那样。但是,在我看来,一个更优雅的解决方案(如果您能够使用 C++17 语言功能)是使用 fold expression 来避免递归。表达式被扩展,以便直接为每个参数调用 Append
,有点像在编译时评估的参数之间的 for 循环。
template <class T>
void Append(std::ostringstream &out, T &&arg) {
out << "," << std::forward<T>(arg);
}
template <class... TArgs>
std::string CreateMessage(TArgs &&...args) {
std::ostringstream out;
(Append(out, std::forward<TArgs>(args)), ...);
return out.str().substr(1);
}
现场演示 here。
混合其他两种解决方案(来自 Passer By 的递归解决方案和来自 Nick Mertin 的 C++17 折叠表达式),您也可以编写 CreateMessage()
而无需递归(以类似的折叠表达式方式)在 C++11 中
std::string const & getStr (std::string const & ret)
{ return ret; }
std::string getStr (int val)
{
std::ostringstream ret;
ret << std::hex << std::setw(4) << std::setfill('0') << val;
return ret.str();
}
template <typename ... Ts>
std::string CreateMessage (Ts const & ... args)
{
using unused = int[];
std::string ret = "";
std::string comma = "";
(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };
return ret;
}
-- 编辑 --
OP 询问
Would you like to teach me how that "simil-fold" line works? How should i "read" it?
嗯...下面是一行
(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };
其中 unused
是 int[]
.
的别名 (using
)
它利用了可变参数包(模板或函数参数)可以在 C 风格数组初始化的上下文中扩展的事实,还利用了逗号运算符的强大功能([= 的逗号运算符的专有性) 74=] 并丢弃逗号左侧的内容)。
所以你有
的扩展(对于 args...
中的每个参数)
( ret += comma + getStr(args), comma = ",", 0 )
哪里
1) 您将 comma + getStr(args)
添加到 ret
,其中 comma
对于第一个参数为空,对于以下参数等于 ","
(请参阅 (2))
2) 第一个逗号运算符丢弃 ret
的值并将 ","
分配给 comma
(所以你在第一个 [=26= 中有一个空的 comma
] 和 ","
以下 ret +=
3) 第二个逗号运算符丢弃逗号和return的值0到unuses
的初始化
因此 ret
递增,所有 getStr(args)
由 ","
分隔,未使用的数组用零初始化。
观察另外两点:
a) 在数组(未命名的 unused
)初始化列表中,您有一个起始且与可变参数无关的零 ({ 0,
);这是必要的,以防 args...
列表为空,因此该行变为 (void)unsed { 0 };
,这是合法的,而不是 (void)unused { };
(没有那个零),这是一个语法错误
b) unused
前面是(void)
;这不是绝对必要的,但有助于避免 "object defined but not used".
类型的警告
我正在尝试在这个 question 中使用 DanielKO 的答案来满足我的需要,但我不熟悉模板和可变参数函数,我不知道我该怎么做。
我需要的是一个可变的 c++(11) 函数,我可以这样调用它:
String NewMsg = CreateMessage("SET",16,1,17,0,"RED",47);
并有 NewMsg= "SET,0010,0001,0011,0000,RED,002F".
我什至不知道应该在参数之间添加逗号的位置。 接着: 我如何在解析 args 时区分整数和字符串,以便将每个整数格式化为十六进制字符串?
你使用递归和函数重载
std::string CreateMessage(int i)
{
return /* i formatted as hex */;
}
std::string CreateMessage(const char* s)
{
return s;
}
template<typename T, typename... Ts>
std::string CreateMessage(T t, Ts... ts)
{
return CreateMessage(t) + "," + CreateMessage(ts...);
}
一种选择是对模板使用递归,正如 Passer By 在他的回答中所做的那样。但是,在我看来,一个更优雅的解决方案(如果您能够使用 C++17 语言功能)是使用 fold expression 来避免递归。表达式被扩展,以便直接为每个参数调用 Append
,有点像在编译时评估的参数之间的 for 循环。
template <class T>
void Append(std::ostringstream &out, T &&arg) {
out << "," << std::forward<T>(arg);
}
template <class... TArgs>
std::string CreateMessage(TArgs &&...args) {
std::ostringstream out;
(Append(out, std::forward<TArgs>(args)), ...);
return out.str().substr(1);
}
现场演示 here。
混合其他两种解决方案(来自 Passer By 的递归解决方案和来自 Nick Mertin 的 C++17 折叠表达式),您也可以编写 CreateMessage()
而无需递归(以类似的折叠表达式方式)在 C++11 中
std::string const & getStr (std::string const & ret)
{ return ret; }
std::string getStr (int val)
{
std::ostringstream ret;
ret << std::hex << std::setw(4) << std::setfill('0') << val;
return ret.str();
}
template <typename ... Ts>
std::string CreateMessage (Ts const & ... args)
{
using unused = int[];
std::string ret = "";
std::string comma = "";
(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };
return ret;
}
-- 编辑 --
OP 询问
Would you like to teach me how that "simil-fold" line works? How should i "read" it?
嗯...下面是一行
(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };
其中 unused
是 int[]
.
using
)
它利用了可变参数包(模板或函数参数)可以在 C 风格数组初始化的上下文中扩展的事实,还利用了逗号运算符的强大功能([= 的逗号运算符的专有性) 74=] 并丢弃逗号左侧的内容)。
所以你有
的扩展(对于args...
中的每个参数)
( ret += comma + getStr(args), comma = ",", 0 )
哪里
1) 您将 comma + getStr(args)
添加到 ret
,其中 comma
对于第一个参数为空,对于以下参数等于 ","
(请参阅 (2))
2) 第一个逗号运算符丢弃 ret
的值并将 ","
分配给 comma
(所以你在第一个 [=26= 中有一个空的 comma
] 和 ","
以下 ret +=
3) 第二个逗号运算符丢弃逗号和return的值0到unuses
因此 ret
递增,所有 getStr(args)
由 ","
分隔,未使用的数组用零初始化。
观察另外两点:
a) 在数组(未命名的 unused
)初始化列表中,您有一个起始且与可变参数无关的零 ({ 0,
);这是必要的,以防 args...
列表为空,因此该行变为 (void)unsed { 0 };
,这是合法的,而不是 (void)unused { };
(没有那个零),这是一个语法错误
b) unused
前面是(void)
;这不是绝对必要的,但有助于避免 "object defined but not used".