模板代码和文本字符串
Template code and literal strings
我目前正在编写一些模板代码,其中模板参数是要使用的字符类型。这会导致引用文本字符串时出现问题。当然,我可以用我使用的字符串做一个结构,但我在想是否有可能做这样的东西:
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
模板参数定义给定字符类型的常量 C 样式字符串?您可以提取字符 但是如何根据模板代码中输入字符串的字符重建新字符串?
请注意,如果您愿意,预处理器可以正确定义带有合适前缀 u 或 U 的字符串,例如"hello world"
,但它无法访问字符串的各个字符来正确翻译它。
编辑:
但是,在新C++中,字符串作为模板参数绝对是可能的, 模板参数未声明为const char *
或类似的东西:
template <char... txt>
struct foo { ... }
允许您将foo<"hello">
编写为类型,并将字符串"hello"
作为模板参数。问题是如何从这些字符构建字符串。
我的意思是在某些时候您希望结构包含要返回的字符串值:
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 字符串文字等相同 这样你就可以写
template <typename ChT>
struct foo {
static const ChT * txta = T<ChT> "AAAAA";
};
编译器会做正确的事情。
这似乎根本不合法,甚至以下内容也被拒绝(vs2017,标准设置为最新):
template<char const * ptr>
struct test
{};
void bar()
{
test<"testing"> t;
}
错误:无效的表达式作为"PTR"的模板参数,如果这不起作用,尝试在编译时转换它是一个非启动器。老实说,指向数据的指针不够恒定似乎并不奇怪。作为模板参数。
以下是一些使其在C++17中工作的工具(可能可移植到 C++11 和 C++14):
模板化类的static constexpr
数据成员
您希望使用的输出文本需要一些"存储"。我建议为每个文字实例化一个唯一的类模板,例如,Literal<char, 'f', 'o', 'o', ' '>. 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', ' '>
。实现此目的的一个有用工具是用户定义的字符串文本的以下重载。
template<class C, C... cs>
constexpr Literal<C, cs..., C(' ')> operator""_c() {// or use `auto`
return Literal<C, cs..., C(' ')>{};
}
请注意如何将输入文本作为非类型模板参数传递给该重载。这样,就可以"将值作为类型携带"。
用于重新编码的constexpr
算法
到目前为止,您可以键入"foo"_c
来获取具有与"foo"
相同的static constexpr
数据成员的Literal<char, 'f', 'o', 'o', ' '>
。接下来,您可以将该Literal<char, 'f', 'o', 'o', ' '>
传递给一个函数,该函数将const char16_t(&)[4]
返回给相应Literal<char16_t, ..., ' '>
data
。语法可以读取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&
,以验证类型和值是否等效于基于所需字符类型的纯字符串文本。
static_assert(std::size(U" ") == 3);
static_assert(std::size(u" ") == 5);
constexpr auto& test = tr<char16_t>(U" "_c);
static_assert(std::is_same<decltype(test), const char16_t(&)[5]>{});
for(std::size_t i=0; i<std::size(u" "); ++i) {
assert(test[i] == u" "[i]);
std::cout << i << ": " << test[i] << std::endl;
}
- 在 C++ 中从 8 位 ASCII 字符创建 7 位 ASCII 文本字符串
- cin>>gender 和 cin>>*gender ( c 样式文本字符串)有什么区别
- 是否可以动态检查文本字符串是否是 C++ 中给定类的成员?
- 用Zlib解压缩文本字符串
- 将文本字符串作为常量字符 * 参数传递会导致代码分析器错误
- 来自文本字符串或某种其他机制的代码类自动生成器
- 函数读取最大和min int值,并用文本字符串返回
- 模板代码和文本字符串
- 声明具有常量引用与常量变量的常量文本字符串
- 加密给定的文本字符串-Caesar Cipher
- 如何使用键盘输入和sf ::文本在SFML中添加一种文本框以显示文本字符串
- 在C++代码中,如何让用户在2个文本字符串之间输入一个数字
- 如何将文本从一个文件复制到另一个文件,然后将文本字符串的第一个字母转换为大写
- 从文本字符串推断类型
- 跟踪内存中的文本字符串
- 将字符串变量与整数和文本字符串连接起来的 C++ 字符串流
- 将 char 数组分配给文本字符串 - C++
- 如何修复将文本字符串附加到 C 字符串的错误
- 使用C++读取不同长度的多个文本字符串
- 在C++中创建原始文本字符串,类似于 C# 的"@ string"