我真的不想写这样难看的代码,如果您有更好的方法,请帮助我进行优化。我真的不想为向量大小写更多的情况,这很愚蠢。
template<typename ... Args>
std::string string_format(const std::string &format, Args... args)
{
size_t size = 1 + snprintf(nullptr, 0, format.c_str(), args ...);
char bytes[size] = {0};
snprintf(bytes, size, format.c_str(), args ...);
return std::string(bytes);
}
std::string test_format(const std::string& str, const std::vector<std::string>& vStr)
{
switch (vStr.size())
{
case 1:
return string_format(str, vStr[0]);
break;
case 2:
return string_format(str, vStr[0], vStr[1]);
break;
case 3:
return string_format(str, vStr[0], vStr[1], vStr[2]);
break;
case 4:
return string_format(str, vStr[0], vStr[1], vStr[2], vStr[3]);
break;
case 5:
return string_format(str, vStr[0], vStr[1], vStr[2], vStr[3], vStr[4]);
break;
case 6:
return string_format(str, vStr[0], vStr[1], vStr[2], vStr[3], vStr[4], vStr[5]);
break;
// ....
default:
return str;
break;
}
}
正如评论中的人所建议,为了帮助清理格式,最好使用适当的格式库,例如libfmt(现在在C ++ 20标准中也有效)
但是,如果您的要求是您拥有运行时 std::vector<std::string>
,并且打算将其转换为compile-time可变参数模板参数序列,那么库可能无法解决此问题您。
由于您要传递包含运行时值的向量,因此在某种程度上需要一种将其转换为编译时列表的方法,以便可以将其传递给string_format
。 is可能,但是需要一些基础结构。
由于可变参数是编译时的效果,所以我们不能简单地将args
转换为args...
。我们至少必须知道我们可以转换成多少个函数的上限。但是,至少可以使用模板来实现。
想法是在compile-time上建立一个跳转图(实际上是一个切换用例),并在运行时对其进行引用。这将要求您必须对可以传递给string_format
的参数数量设置任意限制。
constexpr auto limit = 32; // let's make 32 arguments the arbitrary max
using converter = std::string(*)(const std::string&, const std::vector<std::string>>&);
template <std::size_t...Idxs>
std::string format_vector_impl(const std::string& format, const std::vector<std::string>>& args, std::index_sequence<Idxs...>)
{
return format_string(format, args[Idxs]...);
}
template <std::size_t I>
std::string format_vector(const std::string& format, const std::vector<std::string>>& args)
{
return format_vector_impl(format, args, std::make_index_sequence<I>{});
}
template <std::size_t...Idxs>
constexpr std::array<converter,sizeof...(Idxs)> make_converter_map(std::index_sequence<Idxs...>)
{
return {&format_vector<Idxs>...};
}
std::string test_format(const std::string& format, const std::vector<std::string>>& args)
{
static constexpr auto s_converters = make_converter_map(std::make_index_sequence<limit>{});
assert(args.size() < limit); // if this ever gets triggered, change 'limit'
// use 'args.size()' as the index into the function
return s_converter[args.size()](frmat, args);
}
此解决方案至少使用C ++ 14。
其工作方式如下:
std::string(*)(const std::string&, const std::vector<std::string>&)
将所有转换标准化为format_vector
的签名format_vector<I>
委托给format_vector_impl<Idxs...>
Idxs...
,索引的编译时列表来解压缩参数向量args.size()
作为键调用该功能。通过使用这种方法,您可以使参数的数量与您想要/需要的一样大。
注:使用类似libfmt
的库将有助于解决您的代码当前具有的一些未定义行为,其中args...
正在将std::string
值传递给snprintf
。 snprintf
使用C样式...
变量而不是可变参数template
参数,因此它无法理解或使用C ++类型-仅原始类型(如整数值和const char*
)。
Edit:我刚刚注意到的另一件事是,您当前的代码使用可变长度数组(VLA),它们是not标准C ++。数组需要在编译时固定。在标准C ++中,这不能在运行时完成,但是可以通过某些编译器扩展来完成。我在这里指的是string_format
函数:
size_t size = 1 + snprintf(nullptr, 0, format.c_str(), args ...);
char bytes[size] = {0};
为此,我建议使用std::vector<char>
或什至仅使用std::string
并使用计算的大小调整大小。