C++ Constexpr:将字符串视图列表转换为字符数组列表

C++ Constexpr:将字符串视图列表转换为字符数组列表,c++,c++17,constexpr,C++,C++17,Constexpr,我遇到了一个有趣的constexpr问题,这个问题还没有完全解决。我错过的最后一块拼图是 //给定一个字符串视图的constexpr数组 constexpr std::数组视图=。。。; //将其转换为字符数组列表 constexpr std::元组缓冲区=。。。; 我这里的问题是为每个数组找到合适的大小。如何提取视图中字符串视图的大小,并将其作为模板参数传递给另一个函数 我可以对每个缓冲区使用相同的大小,足够容纳每个参数,但是我想知道是否有一种方法可以优化它们的大小,因为信息在编译时是已知的

我遇到了一个有趣的constexpr问题,这个问题还没有完全解决。我错过的最后一块拼图是

//给定一个字符串视图的constexpr数组
constexpr std::数组视图=。。。;
//将其转换为字符数组列表
constexpr std::元组缓冲区=。。。;
我这里的问题是为每个数组找到合适的大小。如何提取
视图
中字符串视图的大小,并将其作为模板参数传递给另一个函数

我可以对每个缓冲区使用相同的大小,足够容纳每个参数,但是我想知道是否有一种方法可以优化它们的大小,因为信息在编译时是已知的


我试图解决的问题的完整描述。 (以防有人能想出更好的方法,也因为我觉得这很有趣…)

我想创建一个宏,将参数列表转换为名称-值对列表。比如说

//显式添加类型以显示我想要实现的目标。
int x;
浮动y;
双z;
使用命名的\u值\u t=std::tuple<
std::pair,
std::pair,
std::pair>;
命名的_值_t nv=我的_宏(x,y,z);
const char*
增加了很大的难度,但这是第三方库的一个要求

现在我知道Boost.Preprocessor可以实现这一点,但我希望使用STL和constexpr方法来实现这一点,以避免仅仅为此添加Boost。我还知道,在支持
constexpr std::string
的编译器上,这将是微不足道的,但我使用的是C++17

字符串处理可以通过constexpr函数轻松完成

//将“一、二、三”拆分为{“一”、“二”、“三”}。
模板
constexpr std::数组分割参数(std::字符串视图);
但是,我无法将这些字符串视图直接作为字符指针传递,因为此时它们只是指向内存中更大数组的指针(完整的
“一、二、三”
)。要作为
const char*
传递,每个元素都需要以null结尾

但是,我们可以为每个
std::string\u视图
构建一个
std::array
,并复制其内容,这样我们就为每个参数名创建了一个char数组,它将为每个名称生成一个以null结尾的内存段

constepr std::string_view args=“一、二、三”;
constexpr std::array views=split_参数(args);//{“一”、“二”、“三”}
constexpr std::tuple buffers=生成缓冲区(视图);
在这里,我无法理解如何将视图的长度作为模板参数传递给下一个函数

此处的工作解决方案(使用较大的固定大小缓冲区):


固定大小的缓冲区解决方案是可以的,但最好再多做一步,使缓冲区适合其实际大小。

只要
视图
是具有静态存储持续时间的变量(而不是由constexpr函数调用创建的PR值),您可以使用通常的
auto&
template参数和
std::index_序列
技巧来实现这一点:

#include<array>
#include<string_view>
#include<tuple>
#include<utility>
#include<type_traits>
#include<cstddef>

namespace detail {
  template<std::size_t N>
  constexpr auto copy_string(std::string_view s) {
    std::array<char,N+1> ret{};  // zero-initialize
    for(std::size_t i=N;i--;) ret[i]=s[i];
    return ret;
  }
  template<auto &V,std::size_t ...II>
  constexpr auto buffers(std::index_sequence<II...>) {
    return std::make_tuple(copy_string<V[II].size()>(V[II])...);
  }
}

template<auto &V> constexpr auto buffers=
  detail::buffers<V>
  (std::make_index_sequence
   <std::tuple_size_v<std::remove_reference_t<decltype(V)>>>());

constexpr std::array<std::string_view, 3> views = {"C","++",""};

static_assert(std::is_same_v
              <decltype(buffers<views>),
               const std::tuple<std::array<char,2>,
                                std::array<char,3>,
                                std::array<char,1>>>);
static_assert(std::get<0>(buffers<views>)[0]=='C');
static_assert(std::get<1>(buffers<views>)[1]=='+');
static_assert(std::get<2>(buffers<views>)[0]=='\0');
#包括
#包括
#包括
#包括
#包括
#包括
名称空间详细信息{
模板
constexpr自动复制字符串(标准::字符串视图){
std::array ret{};//零初始化
对于(std::size_t i=N;i--)ret[i]=s[i];
返回ret;
}
模板
constexpr自动缓冲区(std::index_序列){
return std::make_tuple(复制字符串(V[II])…);
}
}
模板constexpr自动缓冲区=
细节::缓冲区
(标准::生成索引顺序)
());
constexpr std::array views={“C”、“+”、“};
静态断言(std::is_same_v)
);
静态断言(std::get(buffers)[0]='C');
静态断言(std::get(buffers)[1]='+');
静态断言(std::get(buffers)[0]='\0');

您必须让您的
视图具有静态存储持续时间;可以吗?@Davidsherring你能再详细一点吗?这会有什么帮助?只有当某个值(而不是另一个类型)是模板参数的一部分时,类型才能依赖于该值。类类型的对象,如
视图
不能是模板参数(在C++17中),但它的标识可以(作为引用)-如果它有静态存储持续时间。啊,我明白了,聪明,但还不够,我想,我可以传递一个对静态存储内存位置的引用,但我不能在编译时取消引用它(因为该值尚未初始化),我需要该值来创建
数组