使用_Generic关键字的宏的Eclipse CDT语法错误

时间:2017-08-26 23:26:52

标签: c eclipse-cdt

我正在使用内置CDT 9.3.0的Oxygen。

当我使用我定义的使用_Generic的宏时,所有这些宏用法都用“语法错误”加下划线,但是项目编译得很好(设置为使用我的makefile)。

在阅读similar这样的问题之后,由于_Generic从C11开始可能不受eclipse代码分析的支持,我尝试将我的宏定义的符号定义为空,但它不起作用。 (在项目设置中,C / C ++ General->路径和符号 - >符号选项卡,GNU C,添加了符号CONVERT(...),没有值,并添加了符号CONVERT(X),以及CONVERT()和CONVERT没有价值)。

例如我的宏是:

#define FIRST_(_1, ...) _1
#define FIRST(...) FIRST_(__VA_ARGS__, _1, _2, _3)

#define CONVERT(...)                            \
                _Generic((FIRST(__VA_ARGS__)),  \
                    char*       : toText,   \
                    int         : toInt,    \
                    ) (__VA_ARGS__)

和使用点,它给出了语法错误:

void* test = CONVERT("testme");

1 个答案:

答案 0 :(得分:2)

正如 @ErikW 所指出的那样,_Generic是Eclipse CDT解析器尚不支持的C11功能。 This bug跟踪添加对它的支持。

(顺便说一下,非常欢迎{+ 3}}对Eclipse CDT的C11支持!)

可以使用宏来解决这个问题。

试图在"路径和符号"中定义CONVERT(...)宏的另一个版本的问题就是在那里定义的宏被视为在文件的最顶部写入它们。实际代码中的后续重新定义将覆盖"路径和符号"中的定义。

我可以想到两种解决方法:

方法1

CDT定义了一个特殊的宏__CDT_PARSER__,它在解析代码时求值为true,但在实际编译代码时判断为false。

您可以利用此功能为CDT目的定义不同版本的CONVERT(...)

#ifdef __CDT_PARSER__
    #define CONVERT(...)
#else
    #define CONVERT(...)                            \
                    _Generic((FIRST(__VA_ARGS__)),  \
                        char*       : toText,   \
                        int         : toInt,    \
                        ) (__VA_ARGS__)
#endif

这几乎可行,但并不完全。我们仍然会收到语法错误,因为这一行:

void* test = CONVERT("testme", 42);

现在将扩展为:

void* test = ;

正如您所看到的,我们实际上并不想要CONVERT(...)的空扩展。我们想要一个可以解析为变量初始化程序的扩展。 0将有效:

#ifdef __CDT_PARSER__
    #define CONVERT(...) 0
#else
    ...
#endif

方法2

我们可以将CONVERT(...)本身定义为CDT用途的宏,而不是定义_Generic(...)的不同版本。

这一次,我们可以在"路径和符号"中执行此操作,因为代码中没有_Generic(...)的重新定义会弄乱它。

因此,让我们在"路径和符号"中定义一个符号,其中_Generic(...)为名称和空值。

现在,这一行:

void* test = CONVERT("testme", 42);

将扩展为:

void* test = _Generic((FIRST("testme", 42)),  \
                    char*       : toText,   \
                    int         : toInt,    \
                    ) ("testme", 42)

将依次扩展为:

void* test = ("testme", 42);

解析(("testme", 42)解析为带括号的逗号表达式,因此是一个有效的初始值设定项。)

这种方法的优点是您不需要修改实际代码,并且它处理_Generic宏的所有用途,而不仅仅是CONVERT中的宏。

另一方面,对于_Generic宏的某些其他用途,此特定扩展可能无法解析。如果是这种情况,您可能会想出一个不同的扩展来解析所有用途,否则您可以选择方法1。