Looking at the header, a good point of customization is:
namespace boost { namespace property_tree
{
template <typename Ch, typename Traits, typename E, typename Enabler = void>
struct customize_stream
{
static void insert(std::basic_ostream<Ch, Traits>& s, const E& e) {
s << e;
}
static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
s >> e;
if(!s.eof()) {
s >> std::ws;
}
}
};
it has an Enabler field.
namespace boost { namespace property_tree {
template <typename Ch, typename Traits, typename E>
struct customize_stream<Ch, Traits, E,
std::enable_if_t< /* some test */ >
>
{
static void insert(std::basic_ostream<Ch, Traits>& s, const E& e) {
// your code
}
static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
// your code
}
};
where you can put whatever code in // your code
and whatever test in /* some test */
.
The remaining part is to (a) associate bob::a
with "a"
, and (b) connect this to the above.
I like doing these associations in the namespace of bob
, then finding them via ADL.
Create a template<class T> struct tag_t {}
. If you pass a tag_t<foo>
to a function, ADL will find functions in both the namespace of tag_t
and in the namespace of foo
.
Create a function that gets the mapping from enum value to string (and back). Suppose your mapping is:
std::vector< std::pair< E, std::string > >
and you just do a linear search. Then:
namespace string_mapping {
template<class Enum>
using mapping = std::vector<std::pair< Enum, std::string > >;
}
namespace some_ns {
enum bob { a, b, c };
string_mapping::mapping<bob> const& get_string_mapping( tag_t<bob> ) {
static string_mapping::mapping<bob> retval = {
{bob::a, "a"},
{bob::b, "b"},
{bob::c, "c"},
};
return retval;
}
}
we can find this mapping wherever we have a type T=bob
by doing get_string_mapping( tag_t<T>{} )
.
Use something like can_apply
to detect if get_string_mapping( tag_t<T>{} )
can be found, use that to enable your custom customize_stream
to use get_string_mapping
to load/save the data to/from streams.
Now all we have to do is reduce the pain of writing get_string_mapping
.
#define MAP_ENUM_TO_STRING( ENUM ) \
string_mapping::mapping<ENUM> const& get_string_mapping( tag_t<ENUM> ) { \
static string_mapping::mapping<ENUM> retval =
#define END_ENUM_TO_STRING ; return retval; }
use:
MAP_ENUM_TO_STRING( bob )
{
{bob::a, "a"},
{bob::b, "b"},
{bob::c, "c"},
}
END_ENUM_TO_STRING
within bob
's namespace.
If you want something fancier (ooo, sorted lists, or unordered maps), that can easily be done within get_string_mapping
or even by a get_efficient_string_mapping
that calls get_string_mapping
and does a one-off reprocessing of the flat data.
The big advantage of this is that we don't have to do this in the global namespace; we can put it naturally right under an enum or in the enum's namespace.