2014-03-12 50 views
2

我在写一个Scheme解释器。对于每个内置类型(整数,字符,字符串等),我希望能有统一进行命名的读取和打印功能:使用C预处理器宏命名惯用功能?

READ_ERROR Scheme_read_integer(FILE *in, Value *val); 
READ_ERROR Scheme_read_character(FILE *in, Value *val); 

我想,以确保这些函数的命名一致性

#define SCHEME_READ(type_) Scheme_read_##type_ 
#define DEF_READER(type_, in_strm_, val_) READ_ERROR SCHEME_READ(type_)(FILE *in_strm_, Value *val_) 

所以,现在,而不是上面,在代码中我可以写

DEF_READER(integer, in, val) 
{ 
// Code here ... 
} 

DEF_READER(character, in, val) 
{ 
// Code here ... 
} 

if (SOME_ERROR != SCHEME_READ(integer)(stdin, my_value)) do_stuff(); // etc. 

现在是否被认为是预处理器的单一使用?我在不知不觉中在自己的脚下拍摄自己的脚?我是否应该继续使用这些函数的显式名称?

如果不是有这样的事情做得很好的例子吗?

回答

2

我已经在一个项目中看到了这种广泛的做法,并且存在严重的步行射击的危险。

当您尝试维护代码时,会发生问题。即使您的宏定义函数定义都很整齐,在封面下您可以得到像Scheme_read_integer这样的函数名称。这可能成为一个问题,当崩溃堆栈上出现类似Scheme_read_integer的东西时。如果有人搜索源包Scheme_read_integer,他们不会找到它。这可能会导致很大的痛苦和咬牙切齿;)

如果您是唯一的开发人员,并且代码库不是那么大,并且您还记得使用这种技术几年并且/或者它有很好的文档记录,你可能没有问题。在我的情况下,它是一个很大的代码库,没有文档记录,没有任何原始开发人员。其结果是牙齿咬牙切齿。

我会去上肢体和使用C++模板建议,但我猜,因为你特别提到C.

希望这有助于这不是一个选项。

+0

这帮了很多,谢谢。我可以看到自动生成的名称如何产生维护噩梦。 – user1569339

+0

只需要注意一点:我不确定这将如何转换为C++模板。我会使用静态成员函数或名称空间来获得等价的'Scheme_read_integer(in,val)'=>'Scheme :: Integer :: read(in,val)' – user1569339

+0

它更像'template Scheme_stuff FILE * in,T val)',它将定义一个类,其中'read'成员将类型参数'T'作为参数。它有点冗长,但它减少了一些未来的维护问题。我不得不多想一想,但静态函数可以工作,并且仍然是自我注释。 – Eric

1

我通常是宏的粉丝,但你应该考虑内联包装函数。它们会增加可忽略的运行时间开销,并且会在调试时出现在堆栈回溯等中。