gpt4 book ai didi

c++ - 有什么优雅的方法可以在 C++ 中格式化字符串?

转载 作者:行者123 更新时间:2023-12-02 09:53:37 30 4
gpt4 key购买 nike

关闭。这个问题需要details or clarity .它目前不接受答案。












想改进这个问题?通过 editing this post 添加详细信息并澄清问题.

1年前关闭。




Improve this question




实在不想写这么难看的代码,如果有更好的办法,请帮我优化一下。
我真的不想为 vector 大小写更多的案例,非常愚蠢。

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>并且您打算将其转换为编译时可变参数模板参数序列,那么库可能无法为您解决这个问题。

由于您传入了一个包含运行时值的 vector ,因此有时需要一种方法将其转换为编译时列表,以便可以将其传递给 string_format。 .这是可能的,但是它需要一些基础设施。

由于可变参数是编译时效应,我们不能只转换 argsargs...容易地。至少,我们必须知道我们可以转换成多少函数的上限。然而,这仍然是,至少,使用模板是可能的。

这个想法是在编译时构建一个跳转映射(实际上是一个 switch-case),并在运行时引用它。这将要求您必须对可以传递给 string_format 的参数数量设置任意限制。然而。

#include <string>  // std::string
#include <vector> // std::vector
#include <cstdlib> // std::snprintf
#include <array> // std::array
#include <utility> // std::make_index_sequence
#include <cassert> // assert

template<typename ... Args>
std::string string_format(const std::string &format, Args... args) { ... }

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 string_format(format, args[Idxs].c_str()...);
}

template <std::size_t I>
std::string format_vector(const std::string& format, const std::vector<std::string>& args)
{
assert(I == args.size());

return format_vector_impl(format, args, std::make_index_sequence<I>{});
}

using converter = std::string(*)(const std::string&, const std::vector<std::string>&);

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)
{
// let's make 31 arguments the arbitrary max
// Note: This is '32' here because '0' arguments is also a viable call
static constexpr auto s_limit = 32;
static constexpr auto s_converters = make_converter_map(std::make_index_sequence<s_limit>{});

assert(args.size() < s_limit); // if this ever gets triggered, change 'limit'

// use 'args.size()' as the index into the function
return s_converters[args.size()](format, args);
}

此解决方案假定至少支持 C++14,但是它可以在 C++11 支持中工作并进行一些调整(需要自定义定义 std::index_sequence )

该解决方案的工作原理如下:
  • 将所有转换标准化为 std::string(*)(const std::string&, const std::vector<std::string>&) 的签名通过 format_vector
  • format_vector以 vector 的最大大小为模板
  • 代表format_vector<I>format_vector_impl<Idxs...>通过从 [0...I) 创建一个序列
  • format_vector , 使用 Idxs... ,索引的编译时列表,解包参数 vector
  • 由于所有format_vector函数具有相同的签名,我们可以使用函数指针来构建一个数组。我们在编译时构建它( constexpr )并在运行时调用它。
  • 使用 args.size() 查找正确的处理程序在数组中(实际上是一个跳转图)。

  • 您可以通过使用这种方法使参数的数量尽可能大 - 但请记住,每个新函数都会引入更多生成的汇编代码,这可能会使可执行文件膨胀。

    这是 compiler-explorer 上的一个工作示例

    注:使用像 libfmt 这样的库将有助于解决您的代码当前具有的一些未定义行为,其中 args...正在通过 std::string值为 snprintf . snprintf使用 C 风格 ...可变参数而不是可变参数 template参数,因此它无法理解或使用 C++ 类型——只有原始类型,如整数值和 const char* .

    编辑:我刚刚注意到的另一件事是您当前的代码使用可变长度数组 (VLA),它不是标准 C++。数组需要在编译时修复。这不能在标准 C++ 中在运行时完成,但可以通过一些编译器扩展来完成。我指的是 string_format这里的功能:

        size_t size = 1 + snprintf(nullptr, 0, format.c_str(), args ...);

    char bytes[size] = {0};

    为此,我建议使用 std::vector<char>甚至只是 std::string并使用计算的大小调整大小。就像是:

        auto bytes = std::string{};
    bytes.resize(size, ' ');

    关于c++ - 有什么优雅的方法可以在 C++ 中格式化字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62246208/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com