我经常发现自己在思考"为什么C ++预处理器如此脆弱?"
传统的计数器似乎是" C ++使用的是模板元编程。"但是,有很多情况下模板不会破解它。
例如,我正在编写一个蹦床,它采用 C -struct函数指针表(属于我接口的库)并将函数映射到相应的C ++类的方法。每当对库的更新更改 C -struct时,它都会创建一些维护地狱。我需要在四个不同的地方修改源代码。
除了更新之外,我的代码中存在大量重复,而这些重复无法被环绕。
我真正需要的是生成代码的代码。
那么如何使用类似于Python的东西作为预处理器呢?
#[
funcpointers = []
for i in members(TheCStruct):
if i.name.beginswith("tp_"):
funcpointers.append(i)
def cxxsig(x):
//ToDo
def cxxname(x):
//ToDo
#]
如此直接创建了一个存储所有函数指针的预处理器对象。
稍后我们可以使用这个变量:
class CxxWrapper
{
#[
for i in funcpointers:
print 'virtual ' + cxxsig(i) + '{ std::cout << "' + cxxname(i) + '"ctor\n"; }'
#]
等。因此,上面的示例将生成如下行:
virtual int my_fp(void*, int, float) { std::cout << "my_fp ctor\n"; }
类似的循环将处理构建trampolines等。当底层C-struct发生变化时,一切都保持同步。
可以单步执行预处理器。在预处理器级别进行调试就像调试Python代码一样简单。
所以我的问题是:有没有这样的尝试?如果没有,有什么理由不是吗?如果是这样,结论是什么?
答案 0 :(得分:3)
考虑编写代码生成器 - 它可能特定于此问题或更通用。它不必是预处理器,至少是初始版本。您可以稍后开发/扩展它。
常用代码生成器,包括像这样的情况。要记住的一件事是构建系统中的正确依赖关系,以便重新生成生成的文件等。
答案 1 :(得分:1)
可能没有这样普遍的工具,因为在大多数用例中并不需要它,而在许多其他用例中,它会使源代码更难理解和维护(未来计划很远)
仅供参考这样的预处理器(pcpp
)可以非常简单:
#! /usr/bin/python
"""Python 2.x C and C++ preprocessor."""
import re, sys, textwrap
def ml_escape(msg):
return re.sub(
r'[^\n-+./\w:;<>]', lambda match: '\\%03o' % ord(match.group(0)), msg)
output = []; oa = output.append
def lit(s):
if s.endswith('\n'):
oa('__import__("sys").stdout.write("""%s\\n""")\n' % ml_escape(s[:-1]))
elif s:
oa('__import__("sys").stdout.write("""%s""")\n' % ml_escape(s))
def cod(s): oa(textwrap.dedent(s))
f = open(sys.argv[1])
data, i = f.read(), 0
sc = re.compile(r'(?sm)^[ \t]*#\[[ \t]*$(.*?\n)[ \t]*#\][ \t]*$').scanner(data)
for match in iter(sc.search, None):
j = match.start(0)
lit(data[i : match.start(0)])
cod(data[match.start(1) : match.end(1)])
i = match.end()
lit(data[i : len(data)])
exec compile(''.join(output), f.name, 'exec') in {}
示例输入(t.cp
):
#include <stdio.h>
#[
import re
def c_escape(msg):
return re.sub(
r'[^-+./\w]', lambda match: '\\%03o' % ord(match.group(0)), msg)
def repeat_msg(count, msg):
for i in xrange(count):
print 'puts("%s");' % c_escape(msg)
#]
int main() {
#[
repeat_msg(5, 'hi"')
#]
return 0;
}
示例命令:
$ ./pcpp t.cp >t.c
$ gcc -W -Wall -s -O2 t.c
$ ./a.out
hi"
hi"
hi"
hi"
hi"
示例输出:
#include <stdio.h>
int main() {
puts("hi\042");
puts("hi\042");
puts("hi\042");
puts("hi\042");
puts("hi\042");
return 0;
}
上面的pcpp
实现小心保留换行符,因此如果Python代码中有错误,则回溯将包含文件名t.cp
和正确的行号。通过添加#line
指令的发射,C(++)错误消息中的行号也可以修复。
(请注意,c_escape
与digraphs和trigraphs一样无法正常工作。)