Используйте mpl :: vector в качестве таблицы функций

Я пишу C ++, который читает строку XML и заполняет некоторые элементы структуры значениями, соответствующими ключам атрибутов в файле XML. На данный момент первый проход при этом создает stl :: unordered_map пар ключ-значение. Следующий шаг — интерпретировать строки значений и возвращать их в качестве целевых типов для хранения в структурах. Некоторые типы немного сложны, но у меня есть методы, которые могут их преобразовать.

То, что я хочу сделать, это использовать mpl :: vector для сопоставления ключей с членами структуры, используя методы get_value_ * для преобразования значений. Я думаю, это будет выглядеть примерно так:

typedef boost::mpl mpl;
using namespace std;

template<string K, typename T, T& dest, boost::function<void(MapTypePtr, string, T& dest)> >
struct MapperRow {};

struct Mapper : mpl::vector<
MapperRow < "death_star_radius", Length, death_star.radius, get_value_units >,
MapperRow < "death_star_energy", Energy, death_star.energy, get_value_units >,
MapperRow < "operational",       bool,   death_star.operational, get_value_simple >,
MapperRow < "num_tie_fighters",  int,    death_star.tie_fighers, get_value_simple >
> {};

Типы Length и Energy — это typedefs для boost :: units :: values.

Это выполнимо с метапрограммированием повышения? Если я на правильном пути, как мне заставить его работать? Нужно ли повторять mpl :: vector?

1

Решение

Это определенно возможно (см. Ниже), но я не уверен, что это стоит того … Не могли бы вы использовать более простой подход, возможно, с наследованием и полиморфизмом?

Тем не менее, вот рабочее решение с использованием Boost.MPL:

// MapperRow holds the necessary parsing informations: type of the member,
// type of the object, pointer to the appropriate member, parsing function
template<typename Type, typename Clazz, Type Clazz::*Member,
void (*Parser)(const std::string &, Type &)>
struct MapperRow
{
typedef Type type;
typedef Clazz clazz;
typedef Type Clazz::*memberType;
static const memberType member;
typedef void (*parserType)(const std::string &, Type &);
static const parserType parser;
};

template <typename Type, typename Clazz, Type Clazz::*Member,
void (*Parser)(const std::string &, Type &)>
const typename MapperRow<Type, Clazz, Member, Parser>::memberType
MapperRow<Type, Clazz, Member, Parser>::member = Member;

template <typename Type, typename Clazz, Type Clazz::*Member,
void (*Parser)(const std::string &, Type &)>
const typename MapperRow<Type, Clazz, Member, Parser>::parserType
MapperRow<Type, Clazz, Member, Parser>::parser = Parser;// fill iterates over a map key->MapperRow, trying to find the given key.
// if found, it calls the parsing function, otherwise it asserts false (should
// probably throw an exception instead)
template <typename Clazz, typename First, typename Last>
struct fill_impl
{
static void apply(Clazz &obj, const std::string &key,
const std::string &value)
{
typedef typename mpl::deref<First>::type entry;
static const char *curKey =
mpl::c_str< typename
mpl::first<entry>::type
>::value;

if (key == curKey)
{
typedef typename mpl::second<entry>::type Row;

Row::parser(value, obj.*Row::member);
}
else
{
fill_impl<
Clazz, typename
mpl::next<First>::type,
Last
>::apply(obj, key, value);
}
}
};

template <typename Clazz, typename Last>
struct fill_impl<Clazz, Last, Last>
{
static void apply(Clazz &obj, const std::string &key,
const std::string &value)
{
assert(false && "key not found");
}
};

template <typename Map, typename Clazz>
void fill(Clazz &obj, const std::string &key, const std::string &value)
{
fill_impl<
Clazz, typename
mpl::begin<Map>::type, typename
mpl::end<Map>::type
>::apply(obj, key, value);
}

Образец использования:

template <typename T>
void get_value_units(const std::string &str, T &value)
{
value = T::from_value(boost::lexical_cast<typename T::value_type>(str));
}

template <typename T>
void get_value_simple(const std::string &str, T &value)
{
value = boost::lexical_cast<T>(str);
}

typedef boost::units::quantity<boost::units::si::energy> Energy;
typedef boost::units::quantity<boost::units::si::length> Length;

struct DeathStar
{
Length radius;
Energy energy;
bool operational;
int tie_fighters;
};

// Could be clearer with MPLLIBS_STRING*
typedef mpl::map<
mpl::pair<
mpl::string<'deat','h_st','ar_r','adiu','s'>,
MapperRow< Length , DeathStar, &DeathStar::radius,
&get_value_units<Length> > >,
mpl::pair<
mpl::string<'deat','h_st','ar_e','nerg','y'>,
MapperRow< Energy, DeathStar, &DeathStar::energy,
&get_value_units<Energy> > >,
mpl::pair<
mpl::string<'oper','atio','nal'>,
MapperRow< bool, DeathStar, &DeathStar::operational,
&get_value_simple<bool> > >,
mpl::pair<
mpl::string<'num_','tie_','figh','ters'>,
MapperRow< int, DeathStar, &DeathStar::tie_fighters,
&get_value_simple<int> > >
> death_star_map;

int main()
{
DeathStar ds;
fill<death_star_map>(ds, "death_star_radius", "12");
fill<death_star_map>(ds, "death_star_energy", "34");
fill<death_star_map>(ds, "operational", "1");
fill<death_star_map>(ds, "num_tie_fighters", "56");

std::cout << "Radius: " << ds.radius << '\n';
std::cout << "Energy: " << ds.energy << '\n';
std::cout << "Operational: " << std::boolalpha << ds.operational << '\n';
std::cout << "Tie fighters: " << ds.tie_fighters << '\n';
}

* MPLLIBS_STRING

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]