为什么我们在解析器文件中定义联合时使用复杂 类 的指针?
Why do we use pointers for complex classes when defining union in parser file?
我正在为 RDDL 开发解析器,正如我之前所做的那样,当我定义包含我使用的类型的联合时,我使用指针。例如
%union {
double d;
int i;
std::string *str;
std::vector<std::string> *vectorStr;
RDDLBlock *rddlBlock;
Domain *domain;
DefineType *defineType;
std::vector<DefineType*> *vectorDefineType;
DomainList *domainList;
std::vector<PvarDefinition*> *vectorPvarDefinition;
PvarDefinition *pVarDefinition;
CpfDefinition *cpfDefinition;
std::vector<CpfDefinition*> *vectorCpfDefinition;
PvarExpression *pVarExpression;
LogicalExpression *logicalExpression;
std::vector<LogicalExpression*> *vectorLogicalExpression;
LConstCaseList *lConstCaseList;
CaseDefine *caseDefine;
std::vector<CaseDefine*> *vectorCaseList;
Parameter *parameter;
ParameterList *parameterList;
ObjectDefine *objectDefine;
std::vector<ObjectDefine*> *objectsList;
PvariablesInstanceDefine* pvariablesInstanceDefine;
std::vector<PvariablesInstanceDefine*> *pvariablesInstanceList;
Instance *instance;
NonFluentBlock *nonFluentBlock;
}
这是我看到大多数人在解析器中实现多种标记类型的方式。在网上搜索这个答案时,我看到的只是示例,没有解释为什么我们必须使用指针。我现在的任务之一是尽可能 'clean pointers'。所以我的问题是,为什么在这种情况下我们(必须)在联合中使用指针?
编辑:添加了联合中定义的完整类型列表。
您不必使用指针。如您所见,double
和 int
都不是指针。
至于"why do we use"部分,我们应该记住union
的一些属性。
sizeof union_t
必须至少与最大成员一样大。因此,您不希望按值与单个单词 int
和一些 1KB class 合并。而且指针几乎总是有固定的小尺寸。
在 C++ 世界中,许多 classes(例如,std::string
和 std::vector
)具有非平凡的复制构造函数和析构函数。
对于这样的 classes,将它们合并是不安全的。 C++11为此提供了一个"solution",称为unrestricted unions。但即便如此,它也不会按原样工作:对于 union_t object
的每次赋值和销毁,你必须明确地 destruct/construct 一个活跃的联合成员。
非平凡的对象不能存储在联合中,所以只要使用%union
,就必须使用指针。但是,Bison 3 提供了一种基于变体的替代方案,它使您不必使用指针。
所以不用
%union
{
int ival;
std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;
你会写
%define api.value.type variant
%token <int> NUMBER;
%token <std::string> STRING;
有关详细信息,请参阅 Bison 文档中的 A Complete C++ Example。
我正在为 RDDL 开发解析器,正如我之前所做的那样,当我定义包含我使用的类型的联合时,我使用指针。例如
%union {
double d;
int i;
std::string *str;
std::vector<std::string> *vectorStr;
RDDLBlock *rddlBlock;
Domain *domain;
DefineType *defineType;
std::vector<DefineType*> *vectorDefineType;
DomainList *domainList;
std::vector<PvarDefinition*> *vectorPvarDefinition;
PvarDefinition *pVarDefinition;
CpfDefinition *cpfDefinition;
std::vector<CpfDefinition*> *vectorCpfDefinition;
PvarExpression *pVarExpression;
LogicalExpression *logicalExpression;
std::vector<LogicalExpression*> *vectorLogicalExpression;
LConstCaseList *lConstCaseList;
CaseDefine *caseDefine;
std::vector<CaseDefine*> *vectorCaseList;
Parameter *parameter;
ParameterList *parameterList;
ObjectDefine *objectDefine;
std::vector<ObjectDefine*> *objectsList;
PvariablesInstanceDefine* pvariablesInstanceDefine;
std::vector<PvariablesInstanceDefine*> *pvariablesInstanceList;
Instance *instance;
NonFluentBlock *nonFluentBlock;
}
这是我看到大多数人在解析器中实现多种标记类型的方式。在网上搜索这个答案时,我看到的只是示例,没有解释为什么我们必须使用指针。我现在的任务之一是尽可能 'clean pointers'。所以我的问题是,为什么在这种情况下我们(必须)在联合中使用指针?
编辑:添加了联合中定义的完整类型列表。
您不必使用指针。如您所见,double
和 int
都不是指针。
至于"why do we use"部分,我们应该记住union
的一些属性。
sizeof union_t
必须至少与最大成员一样大。因此,您不希望按值与单个单词int
和一些 1KB class 合并。而且指针几乎总是有固定的小尺寸。在 C++ 世界中,许多 classes(例如,
std::string
和std::vector
)具有非平凡的复制构造函数和析构函数。
对于这样的 classes,将它们合并是不安全的。 C++11为此提供了一个"solution",称为unrestricted unions。但即便如此,它也不会按原样工作:对于 union_t object
的每次赋值和销毁,你必须明确地 destruct/construct 一个活跃的联合成员。
非平凡的对象不能存储在联合中,所以只要使用%union
,就必须使用指针。但是,Bison 3 提供了一种基于变体的替代方案,它使您不必使用指针。
所以不用
%union
{
int ival;
std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;
你会写
%define api.value.type variant
%token <int> NUMBER;
%token <std::string> STRING;
有关详细信息,请参阅 Bison 文档中的 A Complete C++ Example。