Pet*_*der 40 c++ string c-preprocessor c++11
我写了一个可变参数模板,它接受可变数量的char参数,即
template <char... Chars>
struct Foo;
Run Code Online (Sandbox Code Playgroud)
我只是想知道是否有任何宏技巧允许我使用类似于以下语法实例化它:
Foo<"abc">
Run Code Online (Sandbox Code Playgroud)
要么
Foo<SOME_MACRO("abc")>
Run Code Online (Sandbox Code Playgroud)
要么
Foo<SOME_MACRO(abc)>
Run Code Online (Sandbox Code Playgroud)
等等
基本上,任何阻止你单独写字符的东西,就像这样
Foo<'a', 'b', 'c'>
Run Code Online (Sandbox Code Playgroud)
这对我来说不是一个大问题,因为它只是一个玩具程序,但我想我还是会问.
Joh*_*itb 19
我今天创建了一个,并在GCC4.6.0上测试过.
#include <iostream>
#define E(L,I) \
(I < sizeof(L)) ? L[I] : 0
#define STR(X, L) \
typename Expand<X, \
cstring<E(L,0),E(L,1),E(L,2),E(L,3),E(L,4), E(L,5), \
E(L,6),E(L,7),E(L,8),E(L,9),E(L,10), E(L,11), \
E(L,12),E(L,13),E(L,14),E(L,15),E(L,16), E(L,17)> \
cstring<>, sizeof L-1>::type
#define CSTR(L) STR(cstring, L)
template<char ...C> struct cstring { };
template<template<char...> class P, typename S, typename R, int N>
struct Expand;
template<template<char...> class P, char S1, char ...S, char ...R, int N>
struct Expand<P, cstring<S1, S...>, cstring<R...>, N> :
Expand<P, cstring<S...>, cstring<R..., S1>, N-1>{ };
template<template<char...> class P, char S1, char ...S, char ...R>
struct Expand<P, cstring<S1, S...>, cstring<R...>, 0> {
typedef P<R...> type;
};
Run Code Online (Sandbox Code Playgroud)
一些测试
template<char ...S>
struct Test {
static void print() {
char x[] = { S... };
std::cout << sizeof...(S) << std::endl;
std::cout << x << std::endl;
}
};
template<char ...C>
void process(cstring<C...>) {
/* process C, possibly at compile time */
}
int main() {
typedef STR(Test, "Hello folks") type;
type::print();
process(CSTR("Hi guys")());
}
Run Code Online (Sandbox Code Playgroud)
所以当你没有得到a时'a', 'b', 'c',你仍然会得到编译时字符串.
已经有很多试验,但我认为它最终注定要失败.
要理解原因,需要了解预处理器的工作原理.预处理器的输入可以被认为是流.此流首先在预处理标记中进行转换(可在C++编程语言中获得,第3版,附录A语法,第795页)
在这些令牌上,除了digrams/trigrams之外,预处理器可能只应用非常有限数量的操作,这相当于:
#:将一个标记转换为字符串文字标记(用引号括起来)##:连接两个令牌就是这样.
因此,我认为它是不可能的(在C++ 03或C++ 0x中),尽管可能(可能)是针对此的编译器特定扩展.
基于Sylvain Defresne上面的响应的解决方案可以在C++ 11中实现:
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
template <unsigned int N>
constexpr char get_ch (char const (&s) [N], unsigned int i)
{
return i >= N ? '\0' : s[i];
}
#define STRING_TO_CHARS_EXTRACT(z, n, data) \
BOOST_PP_COMMA_IF(n) get_ch(data, n)
#define STRING_TO_CHARS(STRLEN, STR) \
BOOST_PP_REPEAT(STRLEN, STRING_TO_CHARS_EXTRACT, STR)
// Foo <STRING_TO_CHARS(3, "abc")>
// expands to
// Foo <'a', 'b', 'c'>
Run Code Online (Sandbox Code Playgroud)
此外,如果有问题的模板能够处理多个终止'\ 0'字符,我们可以放宽长度要求以支持最大长度:
#define STRING_TO_CHARS_ANY(STR) \
STRING_TO_CHARS(100, STR)
// Foo <STRING_TO_CHARS_ANY("abc")>
// expands to
// Foo <'a', 'b', 'c', '\0', '\0', ...>
Run Code Online (Sandbox Code Playgroud)
上面的例子在clang ++(3.2)和g ++(4.8.0)上正确编译.
基于上面user1653543的解决方案。
一些模板魔术:
template <unsigned int N>
constexpr char getch (char const (&s) [N], unsigned int i)
{
return i >= N ? '\0' : s[i];
}
template<char ... Cs>
struct split_helper;
template<char C, char ... Cs>
struct split_helper<C, Cs...>
{
typedef push_front_t<typename split_helper<Cs...>::type, char_<C>> type;
};
template<char ... Cs>
struct split_helper<'\0', Cs...>
{
typedef std::integer_sequence<char> type;
};
template<char ... Cs>
using split_helper_t = typename split_helper<Cs...>::type;
Run Code Online (Sandbox Code Playgroud)
一些PP魔法:
#define SPLIT_CHARS_EXTRACT(z, n, data) \
BOOST_PP_COMMA_IF(n) getch(data, n)
#define STRING_N(n, str) \
split_helper_t<BOOST_PP_REPEAT(n, SPLIT_CHARS_EXTRACT, str)>
#define STRING(str) STRING_N(BOOST_PP_LIMIT_REPEAT, str)
Run Code Online (Sandbox Code Playgroud)
split_helper只是帮助削减尾随零。现在STRING("Hello")是一个类型化的编译时字符序列 ( std::integer_sequence<char, 'H', 'e', 'l', 'l', 'o'>)。字符串常量的长度最多为BOOST_PP_LIMIT_REPEAT字符。
作业:实现push_front_t并c_str获取std::integer_sequence<char, ...>. (虽然,你可以尝试使用 Boost.MPL)
小智 4
这曾经在 msvc 的早期版本中工作,我不知道它是否仍然有效:
#define CHAR_SPLIT(...) #@__VA_ARGS__
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
14033 次 |
| 最近记录: |