我目前正在使用ANTL4的C#版本到C 目标的某些代码进行移植,目前我遇到了一些问题。我在C#中构建AST的方式是创建一个基类(我们称其为base),并使用虚拟函数派生的类(我们称其为派生),我可以用来实现该类别。
但是,尝试将此代码转换为C ,我一直在获得Bad_cast例外我将其缩小到Antlrcpp ::任何无法正确地将派生的类扔到其基类中的任何人。例如:
class Base {
public:
virtual std::string ToString() const { return "Basen"; }
};
class Derived : public Base {
public:
std::string ToString() const override { return "Derivedn"; }
};
int main() {
Derived value;
std::cout << value.ToString(); //Obviously prints out Derived
Base& base_value = value;
std::cout << base_value.ToString(); //Still works exactly as expected and prints Derived
auto any = antlrcpp::Any(value);
auto derived = any.as<Base>(); //internally uses a dynamic_cast and throws a bad_cast
std::cout << derived.ToString(); //never gets to here
}
我最初认为这可能是因为它仅适用于指针,但是
auto any = antlrcpp::Any(new Derived());
std::cout << any.as<Base*>()->ToString(); //throws bad_cast
我将dynamic_cast
更改为标题内的static_cast
,并将施放,但是它会打印出"基础"。当任何数据成员访问任何数据成员时
我如何确切使用antlrcpp::Any
获得基类?我很明显吗?
,如果不可能,我该如何确切地解决这个问题?有.is()方法,但是有很多情况下,检查访问者的返回值是某种类型的情况是不可行的(例如表达式,可以有30-40个操作员)。
<</p> antlrcpp::any
类不是为这种情况制作的。这不是通用变体实现。
如果您需要使用联合用于各种类型等的联合等方面的变体实现。
在这种情况下,唯一的指针可能不是一个好主意,因为它们不支持复制语义(您在这里需要)。为了与访问者进行评估,最好使用shared_ptr
。
这就是我使它起作用的方式:
有点背景 - 我正在创建一个whizzbang优化的"函数应用程序",将其复杂函数应用于传递的数据记录的类。我不想每次获得新的记录数据,因此我创建了我的优化的whizzbang树,就像对象一样,将功能应用于数据。
现在,此对象和所有派生类的基类都将具有.apply(some_data)函数,因此它们都源自基本类型,但是当调用.apply()时,所有函数都会执行不同的函数,其中包括调用.apply .apply。()属于它们的子对象。
我正在创建的最基本的函数应用对象返回常数数据结构,并在传递数据记录时,用双重表示恒定整数所以:
applier_instance.apply(some_data)
返回:
{23.0, ...}
不管传递了什么数据。通过它{"CAT","DOG","FOO"}
它将返回{23.0, ...}
我希望在Antlr Parser看到字符串" 23"。
现在,如果您走了很远,您将意识到我需要通过多个访问者将该对象传递给,并且在很多时候,这将通过antlrcpp::Any
对象和多次调用非常相似的默认访问者方法这样的调用作为以下ANTLR生成的访客代码中的一个,该代码访问了解析树的"表达式"节点:
virtual antlrcpp::Any visitP_expression(MyParser::P_expressionContext *ctx) override {
return visitChildren(ctx);
}
..最终不在我的"开始"规则中...
virtual antlrcpp::Any visitStart(MyParser::StartContext *ctx) override {
return visitChildren(ctx);
}
就像您一样,我遇到了一个问题,试图通过默认功能移动独特的指针。
我的答案是在我的访问者功能中这样做,以在解析树上处理整数文字:
antlrcpp::Any visitP_NUMBER(MyParser::P_NUMBERContext *ctx) {
IFunctionApplier* fa = new IntegerLiteralApplier(stoi(ctx->getText()));
return fa;
}
我创建了我的原始指针,指向IfunctionApplier接口(已定义了Virtual .Apply(some_data)方法),并创建了一个新的InteGerLiteralApplier对象,该对象从该接口继承。
。我听到的代码上的上一个代码块中的 delete 在哪里?
没有一个 - 我将原始指针变成了一个唯一的指针,一旦它从Antlr生成的函数呼叫的顶部弹出:
...
AttributeMapParser parser(&tokens);
//Get 'start' rule of the parser to make a parse tree (Abstract Syntax Tree)
AttributeMapParser::StartContext* tree = parser.start();
////Walk and visit the tree returning a raw pointer to a function applier
auto fa_raw = visitor.visitStart(tree).as<IFunctionApplier*>();
//convert pointer fa_raw to a unique pointer
auto fa = std::unique_ptr<IFunctionApplier>(fa_raw);
//Clear up the parser and tree
parser.reset();
//start using the unique pointer
auto result = fa->apply(some_input_data);
现在,我警告您,我是C 的初学者,在Python代码上移植了鸭型。
- 它有效
- 一个有25年经验C 经验的人之后看着我的肩膀,说它看上去,我引用"好"
- 网络上的Antlr C 运行时很少,所以我想我会分享我的经验
祝你好运!
P.S。编辑2019/11/13
要展示如何,当上树时,我然后使用在树底部创建的指针:
//For 'not' atom
virtual antlrcpp::Any visitAtom_not(MyParser::Atom_notContext *ctx) override {
auto raw_exp = visit(ctx->atm).as<IFunctionApplier*>();
auto unique_exp = std::unique_ptr<IFunctionApplier>(raw_exp);
IFunctionApplier* fa = new NotApplier(std::move(unique_exp));
return fa;
}
请查看我如何将antlrcpp::Any
施放到界面ifunctionApplier的原始指针,然后将其转换为独特的指针并将其移动到我的父对象中(因此,在这里,字面整数'functionApplier'被传递到"不"应用程序",已成为一个独特的指针。
RAW指针fa
从访问中返回,并将传递到下一个ctx
调用。