我想在c中模拟类,并用宏隐藏实现,但我得到了宏的意外扩展行为。
CREATE TABLE #test
(
Id INT NOT NULL,
LastName VARCHAR(MAX) NOT NULL
)
BULK INSERT #test
FROM 'C:\test.txt'
WITH
(
MAXERRORS = 0,
FIRSTROW = 1,
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\r\n'
)
SELECT *
FROM #test
编译器说我声明了两次struct classMethods(类应该是类的名称)。这意味着当我想要它时,“类”不会被替换。甚至可以这样做吗?
答案 0 :(得分:2)
你的第一个问题是
#define end_methods } ## ;
是语法错误(如果扩展了宏),因为令牌粘贴的结果不是单个有效令牌。您应该收到错误消息,如
error: pasting "}" and ";" does not give a valid preprocessing token
您的第二个问题是在嵌套宏扩展之前执行了令牌粘贴。这意味着你的宏
#define decl_methods struct class ## Methods {
实际上和你写的一样
#define decl_methods struct classMethods {
为了让它做你想做的事,class
必须是类似函数的宏的形式参数:
#define decl_class(class) struct class {
#define end_class(class) }; typedef struct class class;
#define decl_methods(class) struct class ## Methods {
#define end_methods(class) };
#define method(class, returnType, methodName, ...) \
returnType (*methodName)(struct class *self, __VA_ARGS__);
然后
decl_class(Double)
double value;
decl_methods(Double)
method(Double, double, get_value);
end_methods(Double)
end_class(Double)
我想你可以避免在每个宏调用中重复这个类的名称,方法是让一组额外的宏在那里粘贴class
伪参数,但是(由于太多繁琐而无法进入的原因)在这里;非常仔细地阅读Argument Prescan 的" GNU CPP manual"部分,您将需要两个层嵌套扩展获得你想要的效果:
#define decl_class__(class_) struct class_ {
#define decl_class_(class_) decl_class__(class_)
#define decl_class decl_class_(class)
#define decl_methods__(class_) struct class_ ## Methods {
#define decl_methods_(class_) decl_methods__(class_)
#define decl_methods decl_methods_(class)
/* etc */
从技术上讲,只有在最里面的宏需要使用##
(或#
)时才需要这样做,但如果您真的想在真实程序中使用这些宏,那么您应该这样做对所有人都是统一的,否则你会在六个月后撕掉你的头发。
在您通过所有 之后,您会发现您的method
宏不适用于零参数方法,例如
#define class Integer
method(int, getValue)
要么抛出错误,因为在标准C中,宏参数列表中的...
必须至少接收一个参数,否则它会扩展为语法无效的声明,
int (*getValue)(struct Integer *self, );
解决这个问题的唯一方法是使用GNU扩展:
#define method__(class_, returnType, methodName, ...) \
returnType (*methodName)(struct class_ *self, ##__VA_ARGS__);
在GNU扩展C中,##
和,
之间的__VA_ARGS__
具有导致在...
未收到参数时删除逗号的特殊效果。 (这个扩展大约15年前被提议用于标准化,但委员会并不感兴趣。)
此时我邀请您重新考虑仅使用C ++的可能性。