设计用于标记和传输树的 C++ DSL

Sza*_*lcs 5 c++ dsl templates api-design notation

简而言之

我正在尝试为 C 库设计一个更好的 C++ 接口,该库通过通信通道(à la iostreams vs stdio)发送树状表达式。我不确定是否有可能在 C++ 中设计一个 DSL 来标记这些树,同时避免运行时开销,如果是,如何。

C 库的简化说明

有一个 C 库可以通过通信通道发送“表达式”。这里的“表达式”是指可以方便地以类似于函数调用的方式表示的树结构。

例如,

f(1, 2, g(3), "foo")
Run Code Online (Sandbox Code Playgroud)

表示这棵树:

Mathematica 图形

你们中的一些人此时可能认识Mathematica,但我决定将其排除在外,因为它与问题无关。

我们指的f12g(3)参数

要发送此表达式,我们将编写以下内容:

f(1, 2, g(3), "foo")
Run Code Online (Sandbox Code Playgroud)

是否可以为此设计一个具有以下功能的更方便的 C++ API?

  1. 写得更简洁(想想iostreamsvs stdio
  2. 无需明确指定每个头的参数计数;而是使用方便的符号(类似于f(1,2,g(3))上面的),从中它自动推断参数计数
  3. 实现 (2) 没有运行时开销(即在编译时推断参数计数)
  4. 必须在幕后使用上述C接口

我可以用一个类似流的接口来做(1)(即不需要显式指定每个参数的整数、字符串等的类型)。我可以以涉及额外运行时计算的方式执行 (2)。但我不知道考虑到 C++ 的特性,(2)/(3)是否可以一起使用。最终,我希望在 C++ 本身中为这些表达式提供一个方便的符号。

那么是否有可能在 C++ 中为此设计 DSL,同时避免所有运行时开销?如果是,如何?我不一定要寻找以代码为答案的完整解决方案,只是一些入门指南,或者可能有效的方法的摘要。

Pau*_*nan 0

我可以想到几种在 C++ 中表示树的方法。其中一些(#1、#2)的实现将涉及构建中间结构,然后在最后调用 C 函数。其他人(#5、#6)可以随时调用 C 函数,因为任何节点的子节点数量在编译时都是已知的。

1)使用流畅的接口和重载:

Tree()
  .head("f")
    .put(1)
    .put(2)
    .head("g")
      .put(3)
      .end()
    .put("foo")
    .end();
Run Code Online (Sandbox Code Playgroud)

2) 或者,不调用 end():

head("f")
  .put(1)
  .put(2)
  .put(head("g")
    .put(3))
  .put("foo");
Run Code Online (Sandbox Code Playgroud)

3)使用运算符<<:

Tree("f")
  << 1
  << 2
  << (Tree("g") << 3)
  << "foo";
Run Code Online (Sandbox Code Playgroud)

4)使用运算符():

Tree("f")
  (1)
  (2)
  (Tree("g")(3))
  ("foo")
Run Code Online (Sandbox Code Playgroud)

5) 使用 std::initializer_list,其中 Node 是可从 int 和 std::string 隐式转换的类型:

Tree { "f", {
  1,
  2,
  { "g", { 3 } }
  "foo",
} }
Run Code Online (Sandbox Code Playgroud)

6)使用可变参数模板:

head("f").put(
  1,
  2,
  head("g").put(3)
  "foo");
Run Code Online (Sandbox Code Playgroud)