我正在尝试编写一个轻量级库来解析C源代码。
这就是我考虑编写声明解析器的方式:
Decl CParser::ParseDecl(std::istream& in);
或类似的东西:
void CParser::Parse(std::istream& in);
// and
virtual void CParser::OnDecl(const Decl& decl);
其中Decl
是可由TypedefDecl
,FunctionDecl
或VariableDecl
继承的基类。
是否可以将客户端代码强制转换为派生类以获取有关声明的更多信息?或者有更好的方法吗?
修改
函数本身还没有很好地定义,它实际上可能是一个回调,比如CParser::OnDecl(const Decl& decl);
,它可能被像CFomatter: public CParser
之类的派生类重载。这不完全是问题的一部分。
我真的很好奇,如果图书馆的客户必须投放Decl
对象,那就没问题了。在C语言中有很多不同的声明类型(在C ++中甚至更多),似乎为它们中的每一个编写回调或解析器就像必须派生基类一样糟糕。
答案 0 :(得分:0)
首先,你必须避免切片,例如返回一个指针。
Decl* CParser::ParseDecl(std::istream& in);
然后,一般来说,强制客户端转换返回值是设计错误的症状。如果他投的错误怎么办?他怎么知道他必须演出哪种类型?如果用户做错了演员,那就是未定义的行为(以及非常讨厌的错误)。
CParser cp;
...
Decl* token = cp.ParseDecl(ifs);
FunctionDecl *ftoken;
VariableDecl *vtoken;
if (????) { // <============ how does your user know ?
ftoken = static_cast<FunctionDecl>(token);
//... do something with ftoken specific to function declarations
}
else if (????) {
vtoken = static_cast<VariableDecl>(token);
//... do something specific for variable declarations
}
为了使事情更加健壮,您应该至少使类型为多态,具有一个或多个虚函数。然后您的客户可以使用更安全的动态投射并做出正确的决定(如果错误投射则返回nullptr):
...
if (ftoken = dynamic_cast<FunctionDecl>(token)) { // If wrong type fotken will be set to nullptr and you go to the else part
//... do something with ftoken specific to function declarations
}
else if (vtoken = dynamic_cast<VariableDecl>(token)) { // attention: it's = and not ==
//... do something specific for variable declarations
}
这是一个可接受的设计。但是如果你有多态类型,你可以通过利用这种多态来重新思考你的设计,而不是强迫用户处理转换。例如,一种可能的方法是将类特定函数定义为多态函数:
class Decl {
public:
virtual void display_details() { cout << "No detail for this token"; }
...
};
class VariableDecl : public Decl {
...
display_details() { cout<<"This is variable "<<name; }
};
class FunctionDecl : public Decl {
...
display_details() { cout<<"This is function "<<name<<" with "<<nparam<< " parameters"; }
};
然后,用户可以只参考特定的建筑集团,而不需要了解对象的真实类型:
Decl* token = cp.ParseDecl(ifs);
token->display_details();
另一种针对更复杂情况的流行设计是 visitor design pattern 。例如,boost::variant
使用它:如果您不打算使用此库,则可能值得查看它们的示例。