Here is an approach based on Boost.Proto that does work with c++03. It's a slight variation from this example in the documentation.
It can be used like this (sadly not as pretty as the other solution):
// Initialize a map:
Map json =
map_list_of
("foo", "FOO")
("fuu", "FUU")
("bar",
map_list_of
("no", "abc")
("yes", "ABC")
)
("baa",
map_list_of
("no", "xyz")
("yes", "XYZ")
)
;
Proto builds a tree like this:
function(
function(
function(
function(
terminal(map_list_of_tag)
, terminal(foo)
, terminal(FOO)
)
, terminal(fuu)
, terminal(FUU)
)
, terminal(bar)
, function(
function(
terminal(map_list_of_tag)
, terminal(no)
, terminal(abc)
)
, terminal(yes)
, terminal(ABC)
)
)
, terminal(baa)
, function(
function(
terminal(map_list_of_tag)
, terminal(no)
, terminal(xyz)
)
, terminal(yes)
, terminal(XYZ)
)
)
and applies different transforms according to the expression matched. There are four distinct cases:
function(map_list_of,terminal,terminal)
: This is the easiest case. In the example occurs when matching map_list_of("foo","FOO")
.It simply inserts a pair (key,value) into your map and returns a reference.
function(function,terminal,terminal)
: It occurs when matching any pair of terminals that is not the first(map_list_of("foo","FOO")
("fuu","FUU")
). Inserts a pair (key,value) into the map returned from recursively applying the transform to the first child.
function(map_list_of,terminal,function)
: This would occur if there were a nested map in the first pair of parentheses. It creates the nested map and then inserts it with the key terminal in your original map.
function(function,terminal,function)
: This occurs when matching a parenthesized group with a nested map ( map_list_of("foo", "FOO")("fuu", "FUU")
("bar", map_list_of("no", "abc")("yes", "ABC"))
). Creates the nested map and inserts it in the bar
key.
Full Code (Running on Coliru)
#include <map>
#include <string>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/proto/core.hpp>
#include <boost/proto/transform.hpp>
#include <boost/type_traits/add_reference.hpp>
#include <boost/mpl/bool.hpp>
namespace proto = boost::proto;
using proto::_;
using boost::mpl::true_;
using boost::mpl::false_;
struct map_list_of_tag
{};
// A simple callable function object that inserts a
// (key,value) pair into a map.
struct insert
: proto::callable
{
template<typename Sig>
struct result;
template<typename This, typename Map, typename Key, typename Value>
struct result<This(Map, Key, Value)>
: boost::add_reference<Map>
{};
template<typename Map, typename Key, typename Value>
Map &operator()(Map &map, Key const &key, Value const &value) const
{
map.insert(typename Map::value_type(key, value));
return map;
}
};
struct nest
: proto::callable
{
template<typename Sig>
struct result;
template<typename This, typename Map, typename Key, typename Value>
struct result<This(Map, Key, Value)>
: boost::add_reference<Map>
{};
template<typename Map, typename Key, typename Value>
Map& operator()(Map& map, Key const &key, Value const &value) const
{
map[key]=Map();//assign a map to the variant at key
map[key]=value;//assign a map_list_of_expr to a map
return boost::get<Map&>(map[key]); //proto seems to need that this return value be a reference
}
};
// Work-arounds for Microsoft Visual C++ 7.1
#if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
#define MapListOf(x) proto::call<MapListOf(x)>
#define _value(x) call<proto::_value(x)>
#endif
// The grammar for valid map-list expressions, and a
// transform that populates the map.
struct MapListOf
: proto::or_<
proto::when<
// map_list_of(a,b)
proto::function<
proto::terminal<map_list_of_tag>
, proto::terminal<_>
, proto::terminal<_>
>
, insert(
proto::_data //the map you are assigning to
, proto::_value(proto::_child1) //a char const [N] key
, proto::_value(proto::_child2) //a char const [N] value
)
>
, proto::when<
// map_list_of(a,map)
proto::function<
proto::terminal<map_list_of_tag>
, proto::terminal<_>
, MapListOf
>
, insert(
proto::_data //the map you are assigning to
, proto::_value(proto::_child1)
, nest(
proto::_data, //the map you are assigning to
proto::_value(proto::_child1), //a char const [N] key
proto::_child2 //a proto expression (map_list_of_expr) that represents a nested map
)
)
>
, proto::when<
// map_list_of(a,b)(c,d)...
proto::function<
MapListOf
, proto::terminal<_>
, proto::terminal<_>
>
, insert(
MapListOf(proto::_child0) //evaluates the transform recursively
, proto::_value(proto::_child1) //a char const [N] key
, proto::_value(proto::_child2) //a char const [N] value
)
>
, proto::when<
// map_list_of(a,b)(c,map)...
proto::function<
MapListOf
, proto::terminal<_>
, MapListOf
>
, insert(
MapListOf(proto::_child0) //evaluates the transform recursively
, proto::_value(proto::_child1) //a char const [N] key
, nest(
proto::_data, //the map you are assigning to
proto::_value(proto::_child1), //a char const [N] key
proto::_child2 //a proto expression that represents a nested map
)
)
>
>
{};
#if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
#undef MapListOf
#undef _value
#endif
template<typename Expr>
struct map_list_of_expr;
struct map_list_of_dom
: proto::domain<proto::pod_generator<map_list_of_expr>, MapListOf>
{};
// An expression wrapper that provides a conversion to a
// map that uses the MapListOf
template<typename Expr>
struct map_list_of_expr
{
BOOST_PROTO_BASIC_EXTENDS(Expr, map_list_of_expr, map_list_of_dom)
BOOST_PROTO_EXTENDS_FUNCTION()
template<typename Key, typename Value, typename Cmp, typename Al>
operator std::map<Key, Value, Cmp, Al> () const
{
BOOST_MPL_ASSERT((proto::matches<Expr, MapListOf>));
std::map<Key, Value, Cmp, Al> map;
return MapListOf()(*this, 0, map);
}
};
map_list_of_expr<proto::terminal<map_list_of_tag>::type> const map_list_of = {{{}}};
typedef boost::make_recursive_variant<std::string, std::map<std::string, boost::recursive_variant_> >::type Value;
typedef std::map<std::string, Value> Map;
struct printer : boost::static_visitor<void>
{
printer(int indent) :indent(indent) {}
void operator()(const std::string& val) const
{
std::cout << std::string(indent, ' ') << val << std::endl;
}
void operator()(const Map& val) const
{
for (Map::const_iterator it = val.begin(), end = val.end(); it != end; ++it)
{
std::cout << std::string(indent, ' ') << it->first << std::endl;
boost::apply_visitor(printer(indent + 4), it->second);
std::cout << std::string(indent, ' ') << std::endl;
}
}
int indent;
};
void print(const Value& val)
{
boost::apply_visitor(printer(0), val);
}
int main()
{
// Initialize a map:
Map json =
map_list_of
("foo", "FOO")
("fuu", "FUU")
("bar",
map_list_of
("no", "abc")
("yes", "ABC")
)
("baa",
map_list_of
("no", "xyz")
("yes", "XYZ")
)
;
print(json);
return 0;
}