是否可以用C++编写将值绑定(bind)到变量名的edsl?例如,我可以在Haskell中编写edsl,从而允许我编写以下(see also this question):
prog3 :: StackProg Expr
prog3 = do
push (IntL 3)
push (IntL 4)
a <- pop
b <- pop
return (Plus a b)
并生成一个AST,其中
a
和b
是变量。在C++中是否可能有类似的东西?我想要(按重要性顺序)生成的edsl 的
最佳答案
如果要生成有效C++表达式的语法(可能作为所有C++表达式的子集,就像do-notation将其自身限制为monad操作),请对其进行静态验证并使用它们,那么最好的选择是Boost.Proto。简单地说,编写和描述EDSL本身就是EDSL。
我不会更详细地介绍如何使用它。尽管可能很难学会使用它,尤其是如果您不习惯C++元编程,那么该文档非常有用,并且如果您曾经编写语法,我相信您会发现您的标记。在another of my answers中,我向人们介绍了如何使用仅接受简单算术表达式并消耗它们来计算其导数的语法编写EDSL,因此您可能需要检查一下。
至于您的确切问题,恐怕答案要么是简短的“不,您不能那样做”,要么是较长的“您可以在Boost.Phoenix显示的范围内做到这一点,但这可能不是考虑到EDSL用户的密码错误和/或额外的编译时间,值得您花一些时间来实现它”。我这样做的理由是,您想做的事情可以分为两个层次:do-notation是Haskell的特定功能,同时使用了语法树并在EDSL本身的层次上赋予了语义。
碰巧的是,典型的Proto风格的EDSL是有效的C++表达式,并且该语言不提供该范围的作用域,变量在单独的语句中声明。例如,_a + _b
是有效的C++片段,因为_a
和_b
被声明为Phoenix提供的C++变量,但不是EDSL中的有效程序,因为_a
和_b
不受约束。是的,错误将被捕获,但是您必须自己实现。相比之下,do-notation是Haskell的一部分,因此任何EDSL都是免费继承的。也就是说,return (a + b)
永远不会单独有效-需要有一些a
和一些b
。
不过,有些事情要记住。 C++ 11提供了lambda表达式,因此您实际上可以在此处进行范围界定-但是在EDSL中它们将是不透明的,语法树将仅显示变量。一些内省(introspection)可能会发现该变量可用于某些类型,仅此而已。即使您要求lambda在EDSL中返回一个值,也不会说出它们还能做什么。这并不总是值得担心的,我会说它非常适合某些EDSL。
同样,C++ 11使“分解” EDSL表达式的各个部分变得更加容易。这与do-notation的a <- foo
糖并不太等效,但与let a = foo
等效。因此,实际上您可以毫不费力地使以下操作成为“正确的事情”:
auto double_pop = make_tuple(pop(), pop());
auto program = (push(3), push(4), consume(double_pop));
这可能等同于惯用语,并且设计如下:
program = do
let a = pop
return consume `ap` a `ap` a
(由于Boost.Proto从C++ 03库开始,因此在将C++ 11的
auto
与之配合使用之前,请务必先仔细阅读文档,对IIRC来说是一个警告。)关于c++ - C++中具有名称绑定(bind)的EDSL,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14997871/