如何使用元编程生成代码?

时间:2017-11-18 09:02:36

标签: c++ templates metaprogramming

问题: 在我的系统中,有许多统计计算器包装为类。我的工作是将这些计算器的值组合成一个名为“总计”的单个值。

'模型'这解释了如何组合这些计算器作为一个表 - 一个csv文件或MySQL中的表

instance_name,accessor,argument,post_processing_function_name
fleet_statistics,getCash,2017,func1
city_statistics,getPopulation,2016,func2
....
....

我目前的解决方案:我编写了一个python脚本来读取csv文件并生成如下的c ++代码:

double computeTotal()
{
    double total = 0;
    total += func1(fleet_statistics->getCash(2017));
    total += func2(city_statistics->getPopulation(2016));
    ....
    ....
}

使用python生成代码非常方便,但问题是,它不再是纯粹的' c ++程序。我需要破解makefile以确保生成的c ++文件是最新的。

更优雅的解决方案是使用元编程使用c ++而不是python在c ++中生成代码。谁能告诉我怎么做?

Jut提几点:

  1. CSV文件由某些与业务逻辑相关的脚本生成。它有几千行。手动编写c ++代码是不可能的。

  2. 每次发布​​新版本时,CSV文件都会发生变化。

  3. 我以CSV格式为例。它可能是MySQL中的表格,如果这样可以使事情变得更容易

  4. instance_name列中的对象不一定来自同一个BaseClass。访问者的功能签名也是变体。因此使用函数指针

  5. 是不方便的

    谢谢!

4 个答案:

答案 0 :(得分:3)

当CSV文件发生变化时,不需要生成和编译C ++代码。

所有你应该做的就是编写 一次 来解析CSV文件,通过在C ++对象上调用C ++函数来执行CSV文件中指定的计算。

因此,粗略地说,您的fleet_statisticscity_statistics类应该实现一些具有double getYearValueByName( String name, int year )函数的公共接口,该函数检查给定的名称并调用自身的相应函数。同名来获得该年的价值。

我知道这种方法过于简单化,实际上事情比这更复杂;您需要继续扩展该模型,使其变得更加复杂,直到您拥有解析CSV文件并按需要执行所需的所有功能。

答案 1 :(得分:2)

您在评论中提到输入文件的格式在您的控制之下。使用实际元编程(更具体地说是预处理器元编程)的一种方法是将文件格式更改为适合使用X macro模式的格式。像这样:

DATA_POINT(fleet_statistics,getCash,2017,func1)
DATA_POINT(city_statistics,getPopulation,2016,func2)

然后,在您的C ++代码中,您可以这样做:

double computeTotal()
{
    double total = 0;
    #define DATA_POINT(instance_name,accessor,argument,post_processing_function_name) \
      total += post_processing_function_name(instance_name->accessor(argument));
    #include "datafile"
    #undef DATA_POINT
}

这样,编译的C ++代码由预处理器从数据文件生成。

但是,您应该考虑其他评论和答案(我分享)中给出的意见,即解析器可能更适合您的情况。通常,将代码与数据分开是一个好主意。

答案 2 :(得分:1)

  

但问题是,它不再是'纯'c ++

所以?这是一个真正的问题?显然你的情况比简单的“读取csv并采取总和”更复杂。这需要更复杂的解决方案。但是不要因此而需要更复杂。

您正在使用的技术(即通过其他脚本生成C ++代码)没有任何问题。虽然不常见,但仍然可以通过Google's protobuf的显着示例在其他地方看到它。

所以不要寻找其他复杂的解决方案(Jit编译?真的吗?偶数模板通常难以理解)问问自己:我的Python代码生成是否有效?它表现良好吗?容易维护吗?重构成本会是多少?这些都是真正的问题。

另请注意,虽然makefile主要与C / C ++一起使用,但并不严格限制它们。实际上,任何具有一种或多种语言的构建过程都可以使用它们完成。你可以混合它们。这不是黑客行为。这只是一个建设过程。

答案 3 :(得分:0)

首先,您要为CSV文件中的公式编写一些解释器。您可能会找到一个正是这样的库。否则,您需要实现这样的interpreter(或嵌入一些现有的,如LuaGuile)。绝对阅读一些好书,如Dragon BookSICP,然后Lisp In Small Pieces。您需要表示AST和环境(用于变量绑定)。如果你从未接受过有关编译器和口译员的教育,你需要花几周的时间阅读这些技巧(技术成熟但很难,所以你将无法重新发明所有这些技术)。

(我猜你有一个很好的操作系统,比如Linux。如果不是,请调整我的答案)

如果性能真的很重要因为CSV文件中的公式很复杂并且需要时间来计算,请考虑使用某些JIT compilation库将您的AST(从公式)转换为某种形式的代码(例如{{3 }})。

在极少数情况下(但可能不是你的情况)你可能会在运行时将一些C ++生成一些临时文件,然后将其编译为libgccjitplugin临时插件(例如使用Linux上的dynamically load& dlopen。通常这不值得努力,因为C ++编译器非常慢(在编译时!)。生成C代码通常是dlsym(但是YMMV)。但是编译为C或C ++绝非易事,需要数月的开发时间。

  

我需要破解makefile以确保生成的c ++文件是最新的。

不一定(需要破解Makefile,这不是什么大问题)。您可以从Python脚本或C ++程序运行g++(或者您的C ++编译器,如果它不是more appropriate)。我不知道(在你的具体情况下)这样做是明智的。

NB。请注意,在C ++的上下文中,特定元编程意味着(通常)不生成C ++代码或其他代码,但引用GCC