Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;:宏可以展开吗;abc";进入';a'';b'';c';?_C++_String_C++11_C Preprocessor - Fatal编程技术网

C++ C++;:宏可以展开吗;abc";进入';a'';b'';c';?

C++ C++;:宏可以展开吗;abc";进入';a'';b'';c';?,c++,string,c++11,c-preprocessor,C++,String,C++11,C Preprocessor,我已经编写了一个可变模板,它接受数量可变的char参数,即 template <char... Chars> struct Foo; 模板 结构Foo; 我只是想知道是否有任何宏技巧可以让我用类似于以下的语法来实例化它: Foo<"abc"> Foo 或 Foo 或 Foo 等等 基本上,任何能阻止你单独写字符的东西,就像这样 Foo<'a', 'b', 'c'> Foo 这对我来说不是一个大问题,因为这只是一个玩具程序,但我想我还是会问的。这

我已经编写了一个可变模板,它接受数量可变的
char
参数,即

template <char... Chars>
struct Foo;
模板
结构Foo;
我只是想知道是否有任何宏技巧可以让我用类似于以下的语法来实例化它:

Foo<"abc">
Foo

Foo

Foo
等等

基本上,任何能阻止你单独写字符的东西,就像这样

Foo<'a', 'b', 'c'>
Foo

这对我来说不是一个大问题,因为这只是一个玩具程序,但我想我还是会问的。

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

#define CHAR_SPLIT(...) #@__VA_ARGS__

根据我上面讨论的内容,以下可怕的模板黑客可能足以实现这一目标。我还没有测试过这个(对不起!),但我非常肯定它或类似的东西可能会工作

第一步是构建一个只包含字符元组的模板类:

template <char... Chars> class CharTuple {};
模板类CharTuple{};
现在,让我们构建一个适配器,它可以将C样式的字符串转换为图表元组。为此,我们需要以下helper类,它本质上是一个LISP样式的元组cons:

template <typename Tuple, char ch> class Cons;
template <char... Chars, char ch> class Cons<CharTuple<Chars... ch>> {
    typedef CharTuple<ch, Chars...> type;
}
模板类Cons;
模板类Cons{
typedef图表类型;
}
我们还假设有一个meta if语句:

template <bool Condition, typename TrueType, typename FalseType> class If {
    typedef typename TrueType::type type;
};
template <typename TrueType, typename FalseType> class If<False> {
    typedef typename FalseType::type type;
};
模板类,如果{
typedef typename TrueType::type type类型;
};
模板类如果{
typedef typename FalseType::type type类型;
};
然后,您可以使用以下命令将C样式字符串转换为元组:

template <typename T> class Identity {
    typedef T type;
};

template <char* str> class StringToChars {
    typedef typename If<*str == '\0', Identity<CharTuple<>>,
                        Cons<*str, typename StringToChars<str + 1>::type>>::type type;
};
模板类标识{
T型;
};
模板类StringToChars{
typedef typename If::type type;
};
现在,您可以将C样式的字符串转换为字符元组,您可以通过这种类型来恢复元组。不过,我们需要多做一些机械来让它工作。TMP不是很有趣吗?:-)

第一步是使用原始代码:

template <char... Chars> class Foo { /* ... */ };
模板类Foo{/*…*/};
并使用一些模板专门化将其转换为

template <typename> class FooImpl;
tempalte <char... Chars> class FooImpl<CharTuple<Chars...>> { /* ... */ };
模板类FooImpl;
tempalte类FooImpl{/*…*/};
这只是间接的另一层;没别的了

最后,您应该能够做到这一点:

template <char* str> class Foo {
    typedef typename FooImpl<typename StringToChars<str>::type>::type type;
};
模板类Foo{
typedef typename FooImpl::type type;
};

我真的希望这能奏效。如果没有,我仍然认为这是值得发布的,因为它可能ε-接近有效答案。:-)

不幸的是,我认为这是不可能做到的。您可以从预处理器获得的最佳结果是由提供的,最显著的是通过其数据类型:

  • :语法应该是
    (3,(a,b,c))
  • :语法应该是
    (a,(b,(c,BOOST\u PP\u NIL))
  • :语法应该是
    (a)(b)(c)
  • :语法应该是
    (a,b,c)

从这些类型中的任何一种,您都可以轻松创建一个宏,该宏将构建一个逗号分隔的单引号封闭项列表(例如,请参见),但我认为该宏的输入必须是这些类型中的一种,并且所有这些类型都要求单独键入字符。

有很多尝试,但我认为它最终注定要失败

要了解原因,需要了解预处理器是如何工作的。预处理器的输入可以看作是一个流。该流首先在预处理令牌中转换(C++编程语言中的列表可用性,第三版,附件语法,第795页)< /P> 在这些令牌上,预处理器只能应用非常有限数量的操作,除了digrams/trigrams之外,这个数量用于:

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

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

因此,我认为这是不可能的(在C++03或C++0x中),尽管可能(可能)有特定于编译器的扩展。

我今天创建了一个,并在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;
};
#包括
#定义E(L,I)\
(I
一些测试

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")());
}
模板
结构测试{
静态无效打印(){
字符x[]={S..};

std::cout基于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'>
#包括
#包括
模板
constexpr char get_ch(char const&s)[N],无符号整数i)
{
返回i>=N?“\0”:s[i];
}
#定义字符串到字符提取(z,n,数据)\
BOOST_PP_逗号_如果(n)get_ch(数据,n)
#定义字符串到字符(STRLEN,STR)\
BOOST_PP_REPEAT(STRLEN,STRING_TO_CHARS_EXTRACT,STR)
//福
//扩展到
//福
此外,如果所讨论的模板能够处理多个终止“\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', ...>
#定义字符串到字符(STR)\
字符串到字符(100,STR)
//福
//扩展到
//福
上述示例在clang++(3.2)和g++(4.8.0)上正确编译。

基于u
#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'>
#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', ...>
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;
#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)