This is how I made it work:
A bit of background - I am creating a whizzbang optimised "Function Applier" a class that applies its complex function to records of data passed in. I didn't want to visit the parse tree every time I got a new record of data, so I created my optimised whizzbang tree like object to apply the function to the data.
Now the base class of this object and all derived classes will have an .apply(some_data) function so they all derive from the base type, but all perform different functions when .apply() is called, which includes calling .apply() on child objects belonging to them.
The most basic function-applying object I am creating returns a constant data structure with a double representing the constant integer in it when passed a record of data
so:
applier_instance.apply(some_data)
returns:
{23.0, ...}
no matter what data is passed to it. Pass it {"CAT","DOG","FOO"}
it will return {23.0, ...}
I want this applier_instance object to be created when the antlr parser sees the string "23".
Now if you've got this far, you'll realise I need to pass this object up through multiple visitors, and much of the time this will be done via an antlrcpp::Any
object and multiple calls to very similar default visitor methods such as the one in the following antlr generated visitor code, which visits an 'Expression' node of the parse tree:
virtual antlrcpp::Any visitP_expression(MyParser::P_expressionContext *ctx) override {
return visitChildren(ctx);
}
..and finally out of my 'start' rule...
virtual antlrcpp::Any visitStart(MyParser::StartContext *ctx) override {
return visitChildren(ctx);
}
Just like you I ran into an issue trying to move unique pointers up through the default functions.
My answer was to do this in my visitor function for handling integer literals on the parse tree:
antlrcpp::Any visitP_NUMBER(MyParser::P_NUMBERContext *ctx) {
IFunctionApplier* fa = new IntegerLiteralApplier(stoi(ctx->getText()));
return fa;
}
I create my raw pointer to the IFunctionApplier interface (which has the virtual .apply(some_data) method defined) and created a new IntegerLiteralApplier object which inherits from that interface.
Where is the delete for the new in the previous block of code I hear you ask?
There isn't one - I turn my raw pointer into a unique one, as soon as it pops out of the top of the antlr generated function calls:
...
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);
Now I warn you, I'm a beginner at c++, porting over Python code which duck-typed the .apply(some_data) method, so read my answer with some caution, but
- It works
- A guy with 25 years experience of c++ looked over my shoulder afterwards and said it looked, and I quote, "OK"
- There are few resources for antlr c++ runtime on the web, so I thought I'd share my experience
Good luck!
p.s. Edit 2019/11/13
To show how, when going up the tree I then use the pointers created at the bottom of the tree:
//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;
}
See how I am casting the antlrcpp::Any
to a raw pointer to the interface IFunctionApplier, then turning that into a unique pointer and moving it into my parent object (so here a literal integer 'functionapplier' gets passed in to the 'not' 'applier', and has become a unique pointer.
The raw pointer fa
is returned from the visit, and will be passed up to the next ctx
call.
Base
, you can always storeBase*
and dynamic cast it to the needed type on retrieval. – Raffiaantlrcpp:Any((Base*)value)
andany.as<Base*>()
does correctly return a Derived class. It's a little disappointing now because antlrcpp::Any doesn't support unique_ptr and now I have to manage memory. The other way they'd be copied. – Erdmann