I have an abstract struct Base
with no fields (only abstract methods) and struct A
, struct B
inheriting from Base
with different fields in both.
Is it possible to have a rule for parsing either a A
or a B
and store the result in a shared_ptr<Base>
?
I would like to do it in order to parse some A
or B
and store it in a container of shared_ptr<Base>
.
Here is a definition for the structs:
#include <iostream>
using namespace std;
struct Base
{
virtual void operator() const = 0;
};
struct A : Base
{
int value;
void operator() const override { cout << "Hello A: " << x << endl; };
};
struct B : Base
{
float value;
void operator() const override { cout << "Hello B: " << x << endl; };
};
struct BaseContainer
{
multimap<string, shared_ptr<Base>> container;
}
Let's say a BaseContainer
is define by some input formatted like:
name: A value
name: B value
name: A value
where name
is a placeholder for a string used as a key for the multimap
in BaseContainer
, then A
or B
is a keyword for generating a struct A
or struct B
, and value is the value stored in the container.
How would I write a parser BaseContainer
?
The real example I want to apply it to is more complicated, struct A
and struct B
does not have same number of fields in it so please don't answer with something too specific to that example.
Thank you.
So there's two questions here, I feel:
Small demo of approach 2 in the context of this question:
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <map>
#include <iomanip> // std::quoted
struct Base {}; // dynamic polymorphism not required for us now, no problem if you want it
struct A : Base {
int value;
void operator()() const { std::cout << "Hello A: " << value << std::endl; };
};
struct B : Base {
float value;
void operator()() const { std::cout << "Hello B: " << value << std::endl; };
};
using Node = boost::variant<A, B>;
struct BaseContainer {
using Map = std::multimap<std::string, Node>;
Map container;
};
BOOST_FUSION_ADAPT_STRUCT(A, value)
BOOST_FUSION_ADAPT_STRUCT(B, value)
BOOST_FUSION_ADAPT_STRUCT(BaseContainer, container)
namespace qi = boost::spirit::qi;
template <typename It>
struct Parser : qi::grammar<It, BaseContainer()> {
Parser() : Parser::base_type(start) {
using namespace qi;
_key = lexeme['"' >> *('\\' >> char_ | ~char_('"')) >> '"'];
_a_node = "A(" >> int_ >> ")";
_b_node = "B(" >> float_ >> ")";
_node = _a_node | _b_node;
_pair = '{' >> _key >> ',' >> _node >> '}';
_container = '{' >> -(_pair % ',') >> '}';
start = skip(space) [ _container ];
BOOST_SPIRIT_DEBUG_NODES((start)(_container)(_pair)(_key)(_node)(_a_node)(_b_node))
}
private:
qi::rule<It, BaseContainer()> start;
// lexeme
qi::rule<It, std::string()> _key;
using Skipper = qi::space_type;
using Pair = std::pair<std::string, Node>;
qi::rule<It, BaseContainer::Map(), Skipper> _container;
qi::rule<It, Pair(), Skipper> _pair;
qi::rule<It, Node(), Skipper> _node;
qi::rule<It, A(), Skipper> _a_node;
qi::rule<It, B(), Skipper> _b_node;
};
int main() {
Parser<std::string::const_iterator> const p;
for (std::string const input : {
R"({})",
R"({ { "one", A(42) } })",
R"({ { "two", B(3.14) } })",
R"({ { "three", A( -42 ) }, { "four", B( -3.14 ) } })",
})
{
std::cout << "-------\n";
std::cout << "Parsing " << input << "\n";
auto f = begin(input), l = end(input);
BaseContainer result;
if (qi::parse(f, l, p, result)) {
for (auto const& [k,v] : result.container) {
std::cout << " Key " << std::quoted(k) << ": ";
boost::apply_visitor([](auto const& node) { node(); }, v);
}
} else {
std::cout << "Parse failed\n";
}
if (f!=l) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
Prints
-------
Parsing {}
-------
Parsing { { "one", A(42) } }
Key "one": Hello A: 42
-------
Parsing { { "two", B(3.14) } }
Key "two": Hello B: 3.14
-------
Parsing { { "three", A( -42 ) }, { "four", B( -3.14 ) } }
Key "four": Hello B: -3.14
Key "three": Hello A: -42
In my question the base class does not have any attribute, I use it as an interface (like Java's
Interface
), the problem is a little bit different from the link in the first point. Why isn't it possible then?Nobody said it's not possible? If you use it like an interface, then by definition you're talking about the polymorphic aspect. I think my second link addresses that in full detail. The purely static inheritance aspect is trivial with no base data members (just don't mention there is a baseclass, done).
Yes the second answer may be more appropriated in the end. However I have not yet figure out how to make it work with
boost::variant
. ModifyBaseContainer
to storevariant<A, B>
?Do you want to change your question? Maybe you can post a new one if you're stuck with a different AST choice
Thanks a lot, I hope you took you time to lunch! I was away sorry for the delay. So using
variant
and the visitor pattern is the best solution for me, thanks again.