模板代码和文字字符串

时间:2018-01-16 22:30:23

标签: c++ variadic-templates template-specialization

我目前正在编写一些模板代码,其中template参数是要使用的char类型。这在引用文字字符串时会导致问题。当然,我可以使用我使用的字符串制作一个结构,但我在想是否可以制作类似的东西:

template<typename chartype, char32_t... chars>
struct tr {
   /* some magic here */
};

这样tr<char32_t,U"hello world">会导致U"hello world"tr<char16_t,U"Hello world">会产生u"hello world" tr<char,U"hello world">会产生"hello world"(UTF-8)。

魔术当然应该正确地将0x10000以上的代码转换为引导代码并遵循char16_t的代码以及UTF-8的正确2,3和4字节代码 在编译时。

问题是:如何使用char32_t... chars模板参数定义给定char类型的常量C样式字符串?您可以提取字符 从它,但如何根据模板代码中的输入字符串的字符重建一个新的字符串?

注意,预处理器可以正确地定义一个字符串,例如"hello world",如果你愿意,可以使用合适的前缀u或U,但它不能访问字符串的各个字符以正确翻译它。

编辑:

作为模板参数的字符串在新的C ++中肯定是可能的,但是, 模板参数未声明为const char *或类似的东西:

template <char... txt>
struct foo { ... }

允许您将foo<"hello">编写为字符串"hello"作为模板参数的类型。问题是如何从这些字符构建字符串。

我的意思是在某些时候你希望struct包含一个返回的字符串值:

   template <char32_t... txt>
   struct foo;

   template <>
   struct foo<> {
      static const char16_t * txt() { return ""; }
   };

   template <char32_t a, char32_t... rest>
   struct foo<a, rest...> {
   static const char16_t * txt()
   {
        char16_t buf[100];
        int k = 0;
        if (a < 0x10000) buf[k++] = a;
        else {
          buf[k++] = 0xd800 | ((a - 0x10000) >> 10);
          buf[k++] = 0xdc00 | ((a-0x10000) & 0x3ff);
        }
        // copy rest of string into buf + 2..99
        u16strcpy(buf + k, foo<rest...>::txt());
       return buf;
     }
   }

这个“解决方案”有几个明显的问题,一个问题是buf只有100个字符的空间,如果字符串更长则会失败。但主要的问题是我希望这在编译时发生,这看起来非常像我的运行时代码,而不是我想做的。

基本上我想要的东西是这样的:

foo<char, "hello">会产生一些有效的字符串文字 "hello"u8"hello"

foo<char16_t, "hello">会产生一些有效的字符串文字u"hello"foo<char32_t, "hello">会产生有效的字符串文字U"hello"

问题在于编写模板代码以处理各种字符格式,然后涉及字符串文字。是的,你可以写一个简单的结构:

template <typename ChT> struct bar;
   template <>
   struct bar<char32_t> {
     static const char32_t * txta = U"AAAA";
     static const char32_t * txtb = U"BBBB";
   };

依此类推,bar<char16_t>txta = u"AAAA"等。然后参考字符串 在您的模板化代码中bar<T>::txta等等。但是,我希望有一种方法可以直接在模板化代码中指定这些字符串,编译器会做正确的事情。换句话说,是模板化的字符串文字。

也许它应该作为一种功能添加到语言中 T<char32_t> string-literal与U string-literal等相同 这样你就可以写

 template <typename ChT>
 struct foo {
    static const ChT * txta = T<ChT> "AAAAA";
 };

并且编译器会做正确的事。

2 个答案:

答案 0 :(得分:0)

这似乎不合法,即使以下内容被拒绝(vs2017,标准设置为最新):

template<char const * ptr>
struct test
{};
void bar()
{
  test<"testing"> t;
}

错误:无效表达式作为'ptr'的模板参数,如果在编译时尝试转换它不起作用,那么这是一个非启动性。老实说,指向数据的指针不够稳定似乎并不令人惊讶。成为模板参数。

答案 1 :(得分:0)

以下是一些使其在 C ++ 17 中工作的工具(可能可以移植到C ++ 11和C ++ 14):

模板化类的

static constexpr数据成员

您希望使用的输出文字需要一些“存储”。我建议为每个文字实例化一个唯一的类模板,例如Literal<char, 'f', 'o', 'o', '\0'>. That class can hold the data as a静态constexpr`成员。

template<class C, C... cs>
struct Literal {
  static_assert(sizeof...(cs) >= 1);
  static constexpr C data[] = {cs...};// or use `std::array<C, sizeof...(cs)>`
};

template<class C, C... cs>
constexpr C Literal<C, cs...>::data[];

用户定义的字符串文字以简化语法

当然,您希望避免输入内容,例如Literal<char, 'f', 'o', 'o', '\0'>。实现这一目标的一个有用工具是用户定义的字符串文字的以下重载。

template<class C, C... cs>
constexpr Literal<C, cs..., C('\0')> operator""_c() {// or use `auto`
  return Literal<C, cs..., C('\0')>{};
}

注意输入文字如何作为非类型模板参数传递给该重载。这样,就可以“将值作为一种类型”。

用于重新编码的

constexpr算法

到目前为止,您可以输入"foo"_c来获取Literal<char, 'f', 'o', 'o', '\0'>,其中static constexpr数据成员的成员与"foo"相同。接下来,您可以将Literal<char, 'f', 'o', 'o', '\0'>传递给一个函数,该函数会返回相应const char16_t(&)[4]的{​​{1}}到data。语法可以是Literal<char16_t, ..., '\0'>

tr<char16_t>("foo"_c)转换为相应的Literal<char, ...>的代码可以基于Literal<char16_t, ...>算法,如下所示。

constexpr

剩下的部分是实现这些功能template< class OutChar, class InChar, InChar... cs, std::size_t... input_indices, std::size_t... output_indices > constexpr auto& tr_impl(// called by `tr` with appropriate `index_sequence`s Literal<InChar, cs...>, std::index_sequence<input_indices...>, std::index_sequence<output_indices...> ) { constexpr std::size_t outsize = sizeof...(output_indices); using Buffer = std::array<OutChar, outsize>; constexpr Buffer buf = encode_as<OutChar, outsize>({cs...}); return Literal<OutChar, buf[output_indices]...>::data; } template<class OutChar, class InChar, InChar... cs> constexpr auto& tr(Literal<InChar, cs...> literal) { constexpr std::size_t outsize = count_as<OutChar>({cs...}); return tr_impl<OutChar>( literal, std::make_index_sequence<sizeof...(cs)>{},// input indices std::make_index_sequence<outsize>{}// output indices ); } count_as

在最终用法中分配给encode_as

最后,您可以分配给constexpr auto&,以根据所需的字符类型验证类型和值是否等同于普通字符串文字。

constexpr auto&