C++:宏可以将"abc"扩展为"a","b","c"吗?

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',你仍然会得到编译时字符串.

  • @Peter:成千上万的人物?那甚至会编译.我确定你在获得一个大的可变参数模板之前很久就会遇到一些内部编译器限制. (8认同)
  • @Peter它不支持它们.但即使它会:我试图想办法做到这一点,但我不认为没有宏可能.将`a,b,c`传递给模板是一种语法,所以无论如何都需要一个宏.你*可以**在编译时使用带有`constexpr`函数的用户定义文字处理*一个字符串,但该函数的返回类型不能依赖于字符串文字的内容.仅仅归还的价值可以.至少AFAICS. (3认同)

Mat*_* M. 9

已经有很多试验,但我认为它最终注定要失败.

要理解原因,需要了解预处理器的工作原理.预处理器的输入可以被认为是流.此流首先在预处理标记中进行转换(可在C++编程语言中获得,第3版,附录A语法,第795页)

在这些令牌上,除了digrams/trigrams之外,预处理器可能只应用非常有限数量的操作,这相当于:

  • 文件包含(对于头指令),据我所知,这可能不会出现在宏中
  • 宏替换(这是非常复杂的东西:p)
  • #:将一个标记转换为字符串文字标记(用引号括起来)
  • ##:连接两个令牌

就是这样.

  • 没有预处理器指令可以将令牌分成几个令牌:这是宏替换,这意味着实际上首先定义了一个宏
  • 没有预处理器指令将字符串文字转换为常规令牌(删除引号),然后可以进行宏替换.

因此,我认为它是不可能的(在C++ 03或C++ 0x中),尽管可能(可能)是针对此的编译器特定扩展.


use*_*543 9

基于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)上正确编译.


Nev*_*ore 5

基于上面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_tc_str获取std::integer_sequence<char, ...>. (虽然,你可以尝试使用 Boost.MPL)


小智 4

这曾经在 msvc 的早期版本中工作,我不知道它是否仍然有效:

#define CHAR_SPLIT(...) #@__VA_ARGS__
Run Code Online (Sandbox Code Playgroud)