【问题标题】:Syntax Error in Preprocessor Macro Code预处理器宏代码中的语法错误
【发布时间】:2011-03-21 08:41:58
【问题描述】:

我正在尝试为返回字符串长度的宏编写一些代码,并尝试使用 BOOST_PP_WHILE 来实现它。该代码源于这样一个事实,即宏参数foo表示的字符串的position指定位置处的字符可以通过#foo[position]获得。使用 MSVC 或 Intel C++ 进行编译会导致类似的语法错误;如果您能指出代码生成这些语法错误的原因以及我将如何纠正代码,将不胜感激。 我知道错误是由 PREDICATE 宏中的代码引起的,但我尝试在其中使用的任何表达式(除了 BOOST_PP_TUPLE_ELEM)都会导致编译时错误。

错误:

prog.cpp:47:1: error: pasting "BOOST_PP_BOOL_" and ""\"Hello, World!\""" does not give a valid preprocessing token
prog.cpp: In function ‘int main(int, char**)’:
prog.cpp:47: error: ‘BOOST_PP_TUPLE_ELEM_2_1’ was not declared in this scope

正如人们所预料的那样,行号并不是很有用,因为它们都指向调用宏 MACRO_STRLEN 的行。

代码

下面是我尝试实现我描述的宏的源代码列表。

#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/control/while.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <cstdio>

#define TEST_STRING0 "Hello, World!"

#define MACRO_IS_NULL_IMPL(x, position) \
    #x[position] == '\0'

#define MACRO_IS_NULL(x, position) \
    MACRO_IS_NULL_IMPL(x, position)

#define PREDICATE_D(string, position) \
    MACRO_IS_NULL(string, position)

#define PREDICATE(n, state) \
    PREDICATE_D( \
        BOOST_PP_TUPLE_ELEM(2, 0, state), \
        BOOST_PP_TUPLE_ELEM(2, 1, state) \
    )

#define OPERATION_D(string, position) \
    ( \
        string, \
        BOOST_PP_INC(position) \
    )

#define OPERATION(d, state) \
    OPERATION_D( \
        BOOST_PP_TUPLE_ELEM(2, 0, state), \
        BOOST_PP_TUPLE_ELEM(2, 1, state) \
    )

#define MACRO_STRLEN_IMPL(string) \
    BOOST_PP_TUPLE_ELEM( \
        2, 1, BOOST_PP_WHILE(PREDICATE, OPERATION, (string, 0)) \
    )

#define MACRO_STRLEN(string) \
    MACRO_STRLEN_IMPL(string)

int main(int argc, char ** argv) {

    printf("String length: %d.\n", MACRO_STRLEN(TEST_STRING0));
    return 0;

}

【问题讨论】:

  • 在 gcc 4.3.4 下编译会产生一个可能有用的额外错误 - 请参阅我的编辑。
  • 奇怪的是,将两个参数粘贴在一起会导致错误...我尝试修改 PREDICATE 宏的主体,但结果相同。我无法理解为什么 BOOST_PP_TUPLE_ELEM 可以正常工作,但 MACRO_IS_NULL 不能。
  • 上面的错误消息被编辑以反映 OP 编辑​​代码后生成的新错误消息。
  • 很抱歉 - 当我查看编辑时,我想我可能最终撤消了它......

标签: c++ boost c-preprocessor metaprogramming


【解决方案1】:

这个怎么样 - http://codepad.org/aT7SK1Lu 它仍然是一个编译时 strlen,编译起来可能会快得多。

#include <stdio.h>
#include <string.h>

#define TEST_STRING "Hello, World!"

template <int N> struct xtmp2 { typedef char (&t)[N]; };
template< class T, int N > typename xtmp2<N>::t xlen( T (&)[N] );
#define STRLEN(x) (sizeof(xlen(x))-1)

int main( void ) {

  printf( "strlen(\"%s\") = %i %i\n", TEST_STRING, STRLEN(TEST_STRING), strlen(TEST_STRING) );
}

对于宏调试,可以得到一个预处理器输出(如 gcc -E); 取消定义大多数宏也可能会有所帮助,然后一一启用它们以 看看会发生什么。

【讨论】:

  • 为什么不使用模板函数而不是宏?
  • 那么它不会是一个 constexpr - 例如。您将无法定义具有 strlen 大小的数组。实际上在这种情况下更有趣的是为什么我不能在没有 typedef 的情况下返回一个 char 数组。
  • 除非你指定函数返回一个constexpr ?
  • 是的,但是我们也可以用 D 或其他东西编程; C++0x 不值得 atm afaik 的兼容性问题。无论如何,没有 C++0x 标签。
  • 哇,这是一个非常有创意的解决方案。我曾想过尝试使用 Boost MPL 来解决这个问题,但我无法想出一个合适的模板函数。这个解决方案很有趣,因为它不进行任何类型的递归计算,而只是测量定义类型的大小。谢谢!
【解决方案2】:

如果这是一个无关紧要的指出,请原谅我。 BOOST_PP_WHILE 的谓词在预处理时进行评估。 但是,如果我理解正确,MACRO_IS_NULL_IMPL 确定是否 该字符在编译时(运行时?)为'\0'。 所以,我认为直接用字符串很难实现目标 文字"Hello, World!"

【讨论】:

  • 实际上,我认为你在写:) [] 对 C-String 的应用是在运行时评估的(开始时),== 的评估也是如此。跨度>
  • @MatthieuM.:谢谢!这真是令人鼓舞:-D
【解决方案3】:

它不起作用,原因很简单:预处理器不是用来处理文字的。

预处理器只知道“tokens”,它可以连接它们,它可以将一个转换为字符串文字,它可以操作宏替换,但仅此而已。

这里,停止循环的条件(使用[]==)最多可以由编译器执行(并且很可能在运行时),因此不适用于BOOST_PP_WHILE

实际上,您可以使用编译器来获取数组(这里是字符数组)的元素数量:

例如使用sizeofsizeof(array)/sizeof(array[0])。这可以在宏中抽象,但它不能成为“常规”函数,因为数组不能传递给“常规”函数,只能传递给指针(您丢失了信息大小的地方)。

你也可以使用模板函数:

template <typename T, size_t N>
size_t size(T (&)[N]) { return N; }

(这实际上适用于任何具有恒定大小的数组)

但是,对于您自己的问题,您会很高兴知道大多数编译器都有一个内置的 strlen 实现,用于在编译时计算的常量。

【讨论】:

    【解决方案4】:

    我想知道它是否应该是这样的:

    #include <stdio.h>
    #include <string.h>
    
    #define TEST_STRING "Hello, World!"
    
    #define STRLEN(x)    (x[0]==0)?0:TEST_01(x,1)
    #define TEST_01(x,y) (x[y]==0)?y:TEST_02(x,y+1)
    #define TEST_02(x,y) (x[y]==0)?y:TEST_03(x,y+1)
    #define TEST_03(x,y) (x[y]==0)?y:TEST_04(x,y+1)
    #define TEST_04(x,y) (x[y]==0)?y:TEST_05(x,y+1)
    #define TEST_05(x,y) (x[y]==0)?y:TEST_06(x,y+1)
    #define TEST_06(x,y) (x[y]==0)?y:TEST_07(x,y+1)
    #define TEST_07(x,y) (x[y]==0)?y:TEST_08(x,y+1)
    #define TEST_08(x,y) (x[y]==0)?y:TEST_09(x,y+1)
    #define TEST_09(x,y) (x[y]==0)?y:TEST_10(x,y+1)
    #define TEST_10(x,y) (x[y]==0)?y:TEST_11(x,y+1)
    #define TEST_11(x,y) (x[y]==0)?y:TEST_12(x,y+1)
    #define TEST_12(x,y) (x[y]==0)?y:TEST_13(x,y+1)
    #define TEST_13(x,y) (x[y]==0)?y:TEST_14(x,y+1)
    #define TEST_14(x,y) (x[y]==0)?y:TEST_15(x,y+1)
    #define TEST_15(x,y) (x[y]==0)?y:TEST_16(x,y+1)
    #define TEST_16(x,y) (x[y]==0)?y:TEST_17(x,y+1)
    #define TEST_17(x,y) (x[y]==0)?y:TEST_18(x,y+1)
    #define TEST_18(x,y) (x[y]==0)?y:TEST_19(x,y+1)
    #define TEST_19(x,y) (x[y]==0)?y:-1
    
    int main( void ) {
      printf( "strlen(\"%s\") = %i %i\n", TEST_STRING, STRLEN(TEST_STRING), strlen(TEST_STRING) );
    }
    

    但这不是编译时评估,尽管它通常是 优化为常数。

    【讨论】:

      猜你喜欢
      • 2016-09-16
      • 1970-01-01
      • 2011-03-23
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多