用于C++的 Pythonic 预处理器

Pythonic pre-processor for C++

本文关键字:预处理 处理器 Pythonic C++ 用于      更新时间:2023-10-16

我经常发现自己在想"为什么C++预处理器这么弱?

传统的计数器似乎是"C++使用模板元编程代替"。但是,在很多情况下,模板不会破解它。

例如,我目前正在编写一个蹦床,它采用 C 结构函数指针表(属于我正在接口的库)并将函数映射到相应 C++ 类的方法上。每次对库的更新更改 C 结构时,都会创建一些维护地狱。我需要在四个不同的地方修改源代码。

撇开更新不谈,我的代码中存在大量无法绕过的重复项。

我真正需要的是代码来生成代码。

那么使用类似于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) + '"ctorn"; }'
    #]

等。因此,上面的示例将生成如下行:

virtual int my_fp(void*, int, float) { std::cout << "my_fp ctorn"; }

类似的循环将处理建造蹦床等。当底层 C 结构发生变化时,一切都保持同步。

可以单步执行预处理器。在预处理器级别调试与调试 Python 代码一样简单。

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

考虑编写一个代码生成器 - 它可能特定于此问题或更通用。它不必是预处理器,至少是初始版本。您可以稍后开发/扩展它。

代码生成器是常用的,包括像这样的情况。要记住的一件事是构建系统中的正确依赖项,以便重新生成生成的文件等。

可能还没有这样广泛的工具,因为它在大多数用例中都不需要,并且在许多其他用例中,它会使源代码更难理解和维护(未来计划得很远)。

仅供参考 这样的预处理器(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("hi42");
puts("hi42");
puts("hi42");
puts("hi42");
puts("hi42");
  return 0;
}

上面的pcpp实现小心地保留换行符,因此如果您的 Python 代码中有错误,回溯将包含文件名t.cp和正确的行号。通过添加 #line 指令的发出,也可以修复 C(++) 错误消息中的行号。

(请注意,c_escape不能按预期处理二合字母和三合字母。