C++模板的替代方案是什么?

6

我认为元编程非常酷。特别是,我喜欢Lisp宏。

然而,我认为C++模板很糟糕,因为:
1. 它们会减慢编译时间(即使使用预编译头文件,如果包含任何STL内容,它们最终会变成50MB)。
2. 它们会产生令人困惑的可怕编译器/语法错误。
3. 它们一开始就不是为复杂的元编程设计的(在当时生成素数的编译器错误/显示模板是图灵完备的是一个大问题)。

话虽如此,是否有C++元编程的良好替代方案?例如

*.m -> 元编译器 -> *.cpp -> g++ -> 可执行文件?

编辑:

我正在考虑“自定义代码生成脚本”这样的思路。 我只是想知道是否有真正好用的脚本集。


1
你想要生成或计算什么样的东西?结构体?直线函数?面条式代码?状态机? - Potatoswatter
5个回答

7

我不确定这是否符合你的需求,我已经使用代码生成器来生成C++代码。

特别是Python Cheetah。你可以在C++代码中直接嵌入Python代码并通过Cheetah预处理器运行它。相比使用模板或C++预处理器,它可以更轻松地进行相当复杂的计算,还可以获得所有Python库和扩展。但另一方面,如果出现问题,调试会变得更加困难。如果你有兴趣,我可以提供一些示例和用于编辑Cheetah C++程序的Emacs模式。

如果您需要功能较弱且希望留在C++ C内部,请查看boost preprocessor,这里。花一点时间适应它可能很有用,尤其是当涉及到重复代码时。

好的,我正在粘贴Cheetah示例,请给我几分钟时间:

#if defined (__INTEL_COMPILER)
#pragma vector aligned
#endif
        for(int a = 0; a < $N; ++a) {
            /// for functions in block
%for ii, (fi,fj) in enumerate(fb)
%set i = ii + ifb
/// can also use (ix,iy,iz)=fi[0:2], need to clean up when not lazy
%set ix = fi[0]
%set iy = fi[1]
%set iz = fi[2]
%set jx = fj[0]
%set jy = fj[1]
%set jz = fj[2]
            q$(i) += Ix(a,$(ix),$(jx))*Iy(a,$(iy),$(jy))*Iz(a,$(iz),$(jz));
%end for
            /// end for functions in block
        }

运行 cheetah ... 后,会产生以下结果:
#if defined (__INTEL_COMPILER)
#pragma vector aligned
#endif
        for(int a = 0; a < 6; ++a) {
            q0 += Ix(a,0,1)*Iy(a,0,0)*Iz(a,0,0);
            q1 += Ix(a,1,1)*Iy(a,0,0)*Iz(a,0,0);
            q2 += Ix(a,0,1)*Iy(a,1,0)*Iz(a,0,0);
            q3 += Ix(a,0,1)*Iy(a,0,0)*Iz(a,1,0);
            q4 += Ix(a,0,0)*Iy(a,0,1)*Iz(a,0,0);
            q5 += Ix(a,1,0)*Iy(a,0,1)*Iz(a,0,0);
            q6 += Ix(a,0,0)*Iy(a,1,1)*Iz(a,0,0);
            q7 += Ix(a,0,0)*Iy(a,0,1)*Iz(a,1,0);
            q8 += Ix(a,0,0)*Iy(a,0,0)*Iz(a,0,1);
            q9 += Ix(a,1,0)*Iy(a,0,0)*Iz(a,0,1);
        }

这是一段普通的C++代码。

以%开头的行被cheetah预处理器解释为python语句。///是cheetah注释。默认使用#作为python语句,但我更改了它们以避免与C预处理指令冲突。以%end结束python块。以$开头的C++代码中的变量将被替换为python变量。

您需要使用boost预处理器的示例吗?


我之前一直在使用Mako做类似的事情;我在寻找更标准的替代方案时发现了这个。 - Andrew Wagner

4
大多数人坚持从他们喜欢的语言内部进行元编程。由于模板元编程,C++被视为典型例子。虽然它可以工作,但是很痛苦和笨拙。我认为有趣的是,人们在Stroustrop将其添加到语言中后才发现它是图灵完备的;尽管我不认为他预料到它会变成现在这个样子,但他现在肯定不会抱怨。
但是大多数编程语言没有元编程功能(或者它们可能具有薄弱或笨拙的能力 :))。
解决此问题的方法是从语言的外部使用程序转换工具进行元编程。这些工具可以解析源代码,并对其进行任意转换(这就是元编程所做的),然后输出修订后的程序。
如果您拥有一个通用的程序转换系统,该系统可以解析任意语言,那么您可以对/使用任何喜欢的语言进行元编程。 请参阅我们的DMS软件重构工具包,该工具包具有稳健的C、C++(甚至是C++17)、Java、C#、COBOL、PHP和其他编程语言的前端,并已用于所有这些语言的元编程。
这种方法很有用,因为它提供了一种规律性、系统化的方法来为您想要操作的任何语言提供元编程方法。您不必等待语言设计者和实现者去实现,也不必忍受他们能够想象或实际实现的限制,或支付支持它所需的所有反射数据的运行时空/时间代价。 并且程序转换比C++模板元编程更强大,尽管TM具有图灵完备性!原因是TM可以从模板生成任意代码,但无法修改非模板代码。如果您坚持使用TM,程序转换可以模拟TM,因此至少与TM一样强大,但它还可以对非模板代码进行任意更改。因此,严格来说更加强大。
像TM一样,使用程序转换需要一些学习和应用的努力。但是能够以任意方式操纵程序似乎非常有用。
(我们使用DMS对非常大的C++应用程序进行了架构重构。TM根本无法做到这一点)。

2

代码生成是最好的答案...

您还应该查看Linux内核如何处理链表。

详解Linux内核链表

基本思想是,不要将您的类型嵌入到某个结构体中(例如使用下一个和前一个指针的典型列表实现),而是将内核列表结构嵌入到您的结构体中... 这有点令人困惑,但请查看文章... 我从未想过在C中可以实现类型安全的泛型....


除此之外,你的例子不是类型安全的,而是类型无视的。 - rlbond
转换被隐藏在一些宏后面。然而对于 C 而言,在类型安全的方式下很难或者可能不可能做到这种事情,这种技术非常有用。Windows 也有类似的宏(CONTAINING_RECORD())用于链表操作。考虑到 C 的限制,这是一种不错的技巧。现在我学到了一个有用的新术语:“类型无关”。 - Michael Burr
你也可以在C++中使用这种链表技术,而且boost::intrusive让你完全不需要任何转换,甚至不需要隐藏在宏后面的转换。 - Joseph Garvin

1
如果您使用C ++,我认为您唯一可行的选择是直接使用预处理器宏或自定义代码生成
您描述的工作流程基本上相当于某种形式的代码生成,您将对.m文件进行预处理以生成可编译的c ++代码。 SWIG是一个非常好的示例,可以执行此操作的项目。
就个人而言,我在Python中编写代码生成器取得了巨大成功,但我认为任何脚本语言都一样好。 可能有用的一个软件包是我们非常自豪的Ned Batchelder提供的cog

0

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