如何从C风格的Flex解析器转换为C++风格的解析器

4

我有一个典型的可重入的C语言解析器,其中解析后的数据包含在如下union结构中:

%union {
    int number;
    const char *string;
    Item *item_ptr;
}

我想使用共享指针替代普通指针。

由于无法使用C++11编译源代码,也被禁止使用boost::shared_ptr,因此我有自己的类SharedPtr,实现所需的行为。

不幸的是,我无法将SharedPtr类插入以下联合中:

%union {
    int number;
    const char *string;
    SharedPtr<Item> item_ptr;
}

由于我遇到了以下错误:
bisonparser.yy:92:20: error: member ‘SharedPtr<Item> YYSTYPE::item_ptr’ with constructor not allowed in union
bisonparser.yy:92:20: error: member ‘SharedPtr<Item> YYSTYPE::item_ptr’ with destructor not allowed in union
bisonparser.yy:92:20: error: member ‘SharedPtr<Item> YYSTYPE::item_ptr’ with copy assignment operator not allowed in union
bisonparser.yy:92:20: note: unrestricted unions only available with -std=c++11 or -std=gnu++11

另一种方法是插入间接层,如下所示:
%union {
    int number;
    const char *string;
    SharedPtr<Item> *item_ptr;
}

不过,我想知道是否有更加简洁的方式来设计我的项目,以便直接使用我的SharedPtr类而不是作为指针。 我需要找到哪些最小的更改才能得到替代方案?


更广泛地说,在这里完全避免使用联合体并使用不同的设计可能是明智的选择。我猜你并不真正需要联合体所节省的空间吧? - JMAA
1
@RichardChambers https://www.gnu.org/software/bison/manual/html_node/Union-Decl.html - Ted Lyngmo
@JMAA 正确,我不介意使用结构体或类。我正在尝试学习必须应用的最小更改集。 - Anon
我建议的是将 union,这种没有容器智能的粗糙变量存储区域,封装到一个具有容器智能的 variant 类中,并在 %union 中放置指向这样一个类的指针。然后所有操作都将通过 variant 类指针进行,因此目前在 %union 中的内容将被放入新的 variant 类中。但是,我不知道在 Bison 中如何使用 %union。它似乎被用来指定 Bison 生成的源代码中的 union - Richard Chambers
@RichardChambers 即 %union { Node * ptr };,其中 class Node { public: SharedPtr<Item> item_ptr };。使用时会有一些笨重。例如,不是 $$ = new Item() 而是 $$ = new Node(); $$->item_ptr.reset(new Item()),还需要手动删除所有的 Node,可能会像以前一样存在内存泄漏的风险。这就是我对这个设计感到不太满意的原因。 - Anon
显示剩余7条评论
1个回答

4
基本问题在于bison的C接口大量使用联合(来自%union),而C联合与C ++不兼容(在C ++ 11之前,您根本无法将其与非平凡类型一起使用,即使在C ++ 11之后,它们也难以安全地使用)。
一种可能性是使用Bison的C ++模式,但这是一个相当冗长和广泛的改变。或者,您可以(小心地)使用原始指针和其他类型,这些类型可以安全地放置在联合中。但是,您需要非常小心,以避免内存泄漏(并使用bison的%destructor避免语法错误导致的泄漏)。
另一种可能性是根本不使用%union--而是使用#define YYSTYPE SharedPtr<Item>,使堆栈值成为单个共享指针,在代码中到处都使用它。您需要让您的Item类型成为一个基类,所有其他类型都从它继承,并根据需要使用虚函数。

如果使用 #define YYSTYPE Container,其中 Container 是一个包含联合中之前存储的所有成员(包括 SharedPtr<item>)的类,它会起作用吗?我不知道 $$ 在任何时候是否被复制为值,或者只是作为指针/按引用传递。 - Anon
1
@Anon:解析器堆栈是一个 YYSTYPE 数组,其元素经常被复制。此外,它是通过 malloc 动态分配的,并且没有初始化,因此假设您可以复制分配未初始化的存储空间。有一个未记录的钩子,可以让您替换自己的分配器,因此您可以使非平凡对象的 struct 工作。但是,将会有很多不必要的开销来构造未使用的成员。另外,$$ 是一个自动临时变量,它被初始化(通过复制)为 $1;在操作结束时,它被推送到堆栈上(也是复制)。 - rici
@rici 我明白了。感谢你们两个,这个问答对我来说非常有启发性! - Anon
@rici:实际上这取决于您使用的yacc/bison版本——一些(最近的?)在使用C++编译器编译时使用new[]而不是malloc来分配解析器堆栈... - Chris Dodd
@ChrisDodd:你确定吗?我尝试使用bison 3.4.2,没有发现调用new[]来扩展堆栈的证据。初始堆栈(在C和C++中)是一个大小为YYINITDEPTH的自动数组,但如果超过该大小,则会使用malloc执行新分配,并将现有堆栈复制到新分配中。(或者也可能不是这样。取决于您如何定义语义类型,bison可能会拒绝扩展堆栈。) - rici

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