用于C ++的Pythonic预处理器

时间:2015-01-11 11:23:56

标签: python c++ c-preprocessor

我经常发现自己在思考"为什么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代码一样简单。

所以我的问题是:有没有这样的尝试?如果没有,有什么理由不是吗?如果是这样,结论是什么?

2 个答案:

答案 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一样无法正常工作。)