用Python生成C++代码

13

有人可以指导我如何使用Python(或Perl或其他Linux友好的脚本语言)编写脚本以从命令行生成C++代码或从XML或py文件生成代码吗?我想编写一些xml文件,然后运行一个shell命令来读取这些文件并生成带有完全内联函数的.h文件,例如流操作符、构造函数等等。


1
请查看http://www.altova.com/xmlspy/xml-code-generation.html。 - schoetbi
8个回答

16
如果您想仅使用标准Python来完成此操作,可以尝试创建使用Python 3样式字符串格式的模板文件。例如,一个类模板可能如下所示:
{className}::{className}()
{{
}}

{className}::~{className}()
{{
}}

{className}::{className}(const {className}& other)
{{
}}

{className}& {className}::operator=(const {className}& other)
{{
    return *this;
}}

那么你的Python代码非常简单:

d = {}
d['className'] = 'MyCPlusPlusClassName'
with open(yourTemplateFile, 'r') as ftemp:
    templateString = ftemp.read()
with open(generatedFile, 'w') as f:
    f.write(templateString.format(**d))

当然,你可以使用相同的技巧添加许多其他字段作为“className”的附加项。如果你不需要像条件代码生成这样的东西,那么你可以通过使用这种简单方法实现很多功能。


2
很抱歉,您将无法找到一个现成的解决方案来将您特定的xml或python文件转换为所需的输出格式“开箱即用”。您将不得不自己实现解析、数据处理和输出。不过,您并不完全需要自己完成所有工作,下面是一些有关解析和输出的指针。
Python带有两种不同的XML解析器(SAX和DOM-向下滚动以查看一些示例)。您将必须使用其中之一来读取源文件。
为了更轻松地生成输出,您可以使用模板库,例如StringTemplate,或者如果代码很小,可以手动生成代码。

除了StringTemplate之外,还有许多其他模板解决方案最初是针对模板化Web页面的(例如Jinja2),可以相对容易地重新用于这个目的。 - jleahy

1
你可以看一下Shedskin,这是一个从Python代码生成C++代码的项目。
根据你的原因,正如Satanicpuppy所指出的那样,这可能有点毫无意义。

我更感兴趣的是编写一些简单的shell脚本来进行基本的解析和代码生成,如果可能的话。如果不行,我就手写基本类。 - user357525
我不知道比Shedskin更简单的东西了 - 你可能只想自己动手,尽管有些人提到了XML解析器。我想这应该是相当容易的,可以得到一些相当简单的工作。 - Wayne Werner

1

看看猎豹。这是一个用Python编写的模板引擎。


我已经多次查看了Cheetah。尽管它声称用于C++生成,但没有任何关于此的示例,也没有如何将其用于C++的文档。我想编写自己的简单生成器来生成基本数据类型。如果这证明过于复杂,则我不会考虑使用任何代码生成工具。 - user357525

1
几年前,我曾参与一个项目,旨在简化大规模仿真系统的进程间共享内存管理。我们采用了一种相关方法,即在XML文件中定义共享内存中数据的布局,并编写了一个用Python编写的代码生成器,读取XML并输出一组头文件,定义结构和相关函数/运算符等以匹配XML描述。当时,我看了几个模板引擎,令我惊讶的是,手动编写比使用模板引擎更容易、更直接。
当您阅读XML时,只需填充与您的代码匹配的一组数据结构。头文件对象包含类,而类包含变量(可能是其他类类型)。为每个对象赋予一个printSelf()方法,该方法迭代其内容并调用其包含的每个对象的printSelf()
起初似乎有点令人生畏,但一旦开始,就非常直接了当。哦,还有一个提示可以帮助生成的代码,即在printSelf()中添加缩进参数,并在每个级别上增加它。这使得生成的代码更容易阅读。

1
好的,我会尝试这个并且也会看一下StringTemplate。 - user357525
实际上,使用字符串来作为模板生成的一部分是必不可少的。我用简单的%运算符编写了大部分代码,并且它运行得很好,但是新的(是的,代码有点老)StringTemplates应该使它更加容易。 - Rakis

1

根据我的经验,我可以推荐Jinja2(http://jinja.pocoo.org/docs/dev/)。尽管Jinja的主要目标语言是HTML,但它在C++方面也表现得非常出色。这不仅是我的观点,还有 https://www.chromium.org/developers/jinja :). 有一个独立的版本(https://github.com/filwaitman/jinja2-standalone-compiler),可能会很有用,因为Jinja2本身只是一个API。我正在使用独立版本来进行我的项目https://github.com/TomSmartBishop/avl,并进行了自定义环境设置,以使Jinja2的开放和关闭标签更符合C++风格。


Herb Sutter的元类提案可能会在未来过时:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0707r3.pdf(https://www.youtube.com/watch?v=4AfRAVcThyA) - TomSmartBishop

0
希望对某些人有用(你可以使用win32剪贴板读入数据)
import sys, string
import win32clipboard
import re


data = '''
     enum FeedTypeT
     {
         AA,
         BB,
         DDD,
         F
     };
'''

def get_from_clippord():
    # get clipboard data
    win32clipboard.OpenClipboard()
    data = win32clipboard.GetClipboardData()
    win32clipboard.CloseClipboard()
    return data

def enum_type(inenum):
    inenum = inenum.replace('\r\n', '\n')
    inenum = re.sub( r'\s', r'', inenum )
    inenum = re.sub( r'^(.*)\{.*$', r'\1', inenum )
    return inenum

def cleanup_enum(inenum):
    inenum = inenum.replace('\r\n', '\n')
    # inenum = inenum.replace('enum', '')
    inenum = re.sub( r'\s', r'', inenum )
    # inenum = re.sub( r'^.*\{(.+)[|,]\}.*$', r'\1', inenum )
    inenum = re.sub( r'^.*\{(.+)\}.*$', r'\1', inenum )
    inenum = inenum.split(',')
    return inenum

def get_element(inlist):
    for element in inlist:
       [one, two] = element.split('=')
       print('{0:20} ==> {1:>20}'.format(one, two))         # right align
#      one = element.split('=')
#      print('{0:20} ==> {1:10}'.format(one[0], one[1]))

def print_switch(typename):
    retstr = 'const std::string toString( ' + typename + ' type )'
    retstr += '\n{\n   switch( type )\n   {'
    return retstr

def print_case_line(instr, w):
    retstr = '      case ' + '{:{fw}}'.format(instr + ':', fw = w) + ' return "' + instr + '";'
    return retstr

def print_switch_end(w):
    retstr = '      default: ' + ' '*(w-4) + ' return "undef";\n   }\n}\n'
    return retstr

def main():
    #data = get_from_clippord()

    ll = cleanup_enum(data)
    print ( ll )
    print ("="*80 + "\n\n")
    print ( print_switch( enum_type(data)) )

    w = 25
    # pick right with for formating, based on the lenght of elements of enum
    for line in ll:
       if w < len(line):
          w = len(line) + 2

    for line in ll:
       print ( print_case_line(line, w) )

    print ( print_switch_end(w) )


if __name__ == '__main__':
    main()

输出:

['AA', 'BB', 'DDD', 'F']
================================================================================


const std::string toString( enumFeedTypeT type )
{
   switch( type )
   {
      case AA:                       return "AA";
      case BB:                       return "BB";
      case DDD:                      return "DDD";
      case F:                        return "F";
      default:                       return "undef";
   }
}

0

我实际上使用一种遗留的“4GL”开发环境进行一些工作,它们与此类似,并且旧的4GL范例中有很多模型将C和C++用作所输出生成代码的语言。

话虽如此,4GL几乎已经被放弃了。问题在于,当你机器生成C代码时,你会失去原本使用C所带来的性能提升,并且这些代码难以维护。最好一开始就用Python编写程序。


我不是在寻找一个程序转换器。我在寻找一份关于如何编写自己的Python/Perl脚本的文档,该脚本将仅基于短数据类型规范生成具有基本功能(如流运算符)的C++数据类型。该规范仅列出类名、成员、基类和每个成员的简单文档。输出将是一个具有完全内联的类定义的头文件。我认为Python或Perl应该能够轻松完成这个任务。 - user357525

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接