一个可变参数函数,它接受字符串和整数,将后者格式化并连接所有字符串?

时间:2019-01-28 14:56:56

标签: c++ c++11 variadic-templates variadic-functions

我正试图在question中使用DanielKO的答案满足我的需要,但是我对模板和可变参数函数不熟悉,并且我不知道该怎么做。

我需要一个可变的c ++(11)函数,我可以这样调用它:

 String NewMsg = CreateMessage("SET",16,1,17,0,"RED",47);

并具有NewMsg =“ SET,0010,0001,0011,0000,RED,002F”。

我什至无法在参数之间添加逗号。 接着: 解析args时,如何区分整数和字符串,以便将每个整数格式化为十六进制字符串?

3 个答案:

答案 0 :(得分:8)

您使用递归和函数重载

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

答案 1 :(得分:3)

一种选择是对模板使用递归,就像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

答案 2 :(得分:2)

混合其他两种解决方案(来自Passer By的递归解决方案和来自Nick Mertin的C ++ 17折叠表达式),您也可以在C中编写CreateMessage(),而无需进行递归(以类似折叠表达方式) ++ 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询问

  

您想教我“类似折叠”字样的工作方式吗?我应该如何“阅读”它?

嗯...下面是这行

(void)unused { 0, ( ret += comma + getStr(args), comma = ",", 0 )... };

其中unusedusing的别名(int[])。

它使用了可变参数包(模板或函数自变量)可以在C样式数组初始化的上下文中进行扩展的事实,并且还使用了逗号运算符的功能(exec / compute和丢弃逗号左侧的内容。

因此(对于args...中的每个参数,您都有

的扩展)
( ret += comma + getStr(args), comma = ",", 0 )

其中

1)在comma + getStr(args)上添加ret,其中第一个参数comma为空,随后的参数等于","(请参阅(2))

2)第一个逗号运算符丢弃ret的值,并将","分配给comma(因此,前comma中有一个空白ret +=","用于关注ret +=

3)第二个逗号运算符舍弃逗号的值,并将0返回到unuses的初始化

因此ret递增,而所有getStr(args)","隔开,未使用的数组用零初始化。

还要注意几点:

a)在数组(未命名的unused)初始化列表中,您有一个起始且与变参无关的零({ 0,);如果args...列表为空,则此行是必需的,因此该行变为(void)unsed { 0 };,这是合法的,而不是(void)unused { };(不带零),这是一个语法错误

b)unused之前是(void);这不是严格必要的,但是对于避免发出“对象已定义但未使用”类型的警告很有用。