我看到两种主要方法:
使用编译时“反射”
您可以使用 BOOST_FUSION_ADAPT_STRUCT 并鱼与熊掌兼得。如果你调整你的结构,你可以静态地迭代它们 - 事实上编写 @πάνταῥεῖ 提到的代码生成器,但与 C++ 代码内联并在编译时。
您可以使用变体对类型进行静态约束。
-
是否可以从适应的结构生成融合图? https://stackoverflow.com/questions/19657065/is-it-possible-to-generate-a-fusion-map-from-an-adapted-struct/19658340#19658340显示如何使用
fusion::extension::struct_member_name
转储适应 Fusion 的通用结构(您可以忘记类型名称分解,因为您不需要它)
-
增强结构和类的融合序列类型和名称识别 https://stackoverflow.com/questions/26380420/boost-fusion-sequence-type-and-name-identification-for-structs-and-class/26381145#26381145显示 Fusion 适应结构的类似 XML 的输出。
附有语法手册
使用 Boost Spirit,您可以为相同的结构创建语法:
start = *(label_(+"object") >> object_);
object_ = foo_ | bar_;
foo_ = "Foo" >> eol >> (
(string_prop_(+"name") >> eol) ^
(int_prop_(+"age") >> eol)
);
bar_ = "Bar" >> eol >> (
(string_prop_(+"location") >> eol)
);
label_ = lit(_r1) >> ':';
string_prop_ = label_(_r1) >> lexeme [ *(char_ - eol) ];
int_prop_ = label_(_r1) >> int_;
现在这解析成variant<Foo, Bar>
无需任何进一步的编码。它甚至允许name
and age
以随机顺序出现(或接受默认值)。当然,如果你不这样做want这种灵活性,取代^
with >>
在语法中。
这是一个示例输入:
object: Foo
name: Joe Bloggs
age: 26
object: Foo
age: 42
name: Douglas Adams
object: Foo
name: Lego Man
object: Bar
location: UK
这是示例(调试)输出的尾部:
<success></success>
<attributes>[[[[J, o, e, , B, l, o, g, g, s], 26], [[D, o, u, g, l, a, s, , A, d, a, m, s], 42], [[L, e, g, o, , M, a, n], 0], [[U, K]]]]</attributes>
</start>
Parse success: 4 objects
N4data3FooE (Joe Bloggs 26)
N4data3FooE (Douglas Adams 42)
N4data3FooE (Lego Man 0)
N4data3BarE (UK)
Live On Coliru http://coliru.stacked-crooked.com/a/2a7b6bda0049aa7e
#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/bind.hpp>
#include <fstream>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace demo {
struct visitor : boost::static_visitor<> {
template<typename Seq>
void operator()(std::ostream& os, Seq const& seq) const {
os << typeid(Seq).name() << "\t" << boost::fusion::as_vector(seq);
}
};
}
namespace data {
struct Foo {
Foo(std::string n="", int a=0) : name(n), age(a) {}
std::string name;
int age;
};
struct Bar {
Bar(std::string l="") : location(l) {}
std::string location;
};
using object = boost::variant<Foo, Bar>;
using objects = std::vector<object>;
std::ostream& operator<< (std::ostream& os, object const& o) {
boost::apply_visitor(boost::bind(demo::visitor(), boost::ref(os), _1), o);
return os;
}
}
BOOST_FUSION_ADAPT_STRUCT(data::Foo,(std::string,name)(int,age))
BOOST_FUSION_ADAPT_STRUCT(data::Bar,(std::string,location))
template <typename It>
struct grammar : qi::grammar<It, data::objects(), qi::blank_type> {
grammar() : grammar::base_type(start) {
using namespace qi;
start = *(label_(+"object") >> object_);
object_ = foo_ | bar_;
foo_ = "Foo" >> eol >> (
(string_prop_(+"name") >> eol) ^
(int_prop_(+"age") >> eol)
);
bar_ = "Bar" >> eol >> (
(string_prop_(+"location") >> eol)
);
label_ = lit(_r1) >> ':';
string_prop_ = label_(_r1) >> lexeme [ *(char_ - eol) ];
int_prop_ = label_(_r1) >> int_;
BOOST_SPIRIT_DEBUG_NODES((start)(object_)(foo_)(bar_)(label_)(string_prop_)(int_prop_));
}
private:
qi::rule<It, data::objects(), qi::blank_type> start;
qi::rule<It, data::object(), qi::blank_type> object_;
qi::rule<It, data::Foo(), qi::blank_type> foo_;
qi::rule<It, data::Bar(), qi::blank_type> bar_;
qi::rule<It, std::string(std::string), qi::blank_type> string_prop_;
qi::rule<It, int(std::string), qi::blank_type> int_prop_;
qi::rule<It, void(std::string), qi::blank_type> label_;
};
int main()
{
using It = boost::spirit::istream_iterator;
std::ifstream ifs("input.txt");
It f(ifs >> std::noskipws), l;
grammar<It> p;
data::objects parsed;
bool ok = qi::phrase_parse(f,l,p,qi::blank,parsed);
if (ok)
{
std::cout << "Parse success: " << parsed.size() << " objects\n";
for(auto& object : parsed)
std::cout << object << "\n";
} else
{
std::cout << "Parse failed\n";
}
if (f!=l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}