Вопрос

I have:

  • A template routine unarchive that takes a dictionary and a key and based on the template type (T) passed can be specialized to produce a T
  • A constructor for a struct that leverages unarchive to construct its members

An example might be the following:

template <typename T>
T unarchive(const dictionary_t&, key_type key);

struct foo
{
    foo(const dictionary& archive) :
        value_m(unarchive<decltype(value_m)>(archive, value_key))
    { }

    some_value_type value_m;
};

The advantage of using unarchive<decltype(value_m)> here is that I can change the type of value_m without having to update this line of code - the type always follows the member variable's type.

The problem I have is more aesthetic: it is very verbose. Currently I have a macro:

#define UNARCHIVE_FOR(var) unarchive<decltype(var)>

And the foo's constructor changes as follows:

foo(const dictionary& archive) :
    value_m(UNARCHIVE_FOR(value_m)(archive, value_key))
{ }

Now I have a result that is more terse but far uglier. Can the same result be achieved without a macro? What I would like would be something akin to:

foo(const dictionary& archive) :
    value_m(unarchive<value_m>(archive, value_key))
{ }

How can this be done?

Это было полезно?

Решение

The advantage of using unarchive here is that I can change the type of value_m without having to update this line of code - the type always follows the member variable's type.

One alternative is to create an alias for the type of value_m and eliminate the decltype(value_m) from the constructor initializer list:

struct foo
{
    using value_type = int;

    foo(const dictionary_t& archive, const key_type value_key) :
        value_m(unarchive<value_type>(archive, value_key))
    { }

    value_type value_m;
};

the unarchive<value_type> still follows the type of value_m. A static_assert could be added to ensure the type of value_m is the same as value_type if there is concern of a change to the type of value_m by not changing value_type:

static_assert(std::is_same<decltype(value_m), value_type>::value,
              "'value_m' type differs from 'value_type'");

or set the alias based on the type of value_m:

int value_m;
using value_type = decltype(value_m);

If you still consider the constructor initialization list verbose provide a static wrapper function that invokes the unarchive() function:

struct foo
{
    using value_type = int;

    foo(const dictionary_t& archive, const key_type value_key) :
        value_m(unarchive_(archive, value_key))
    { }

    static value_type unarchive_(const dictionary_t& d, key_type k)
    {
        return unarchive<value_type>(d, k);
    }

    value_type value_m;
};

Having said all that:

value_m(unarchive<decltype(value_m)>(archive, value_key))

is not that verbose and precisely states the intention.

Другие советы

It's a bit hackish, but how about introducing a wrapper class for the archive with a templated conversion operator:

class wrapper {
  const dictionary_t& dict_m;
  const key_type key_m;
public:
  wrapper(const dictionary_t& d, key_type k) :
    dict_m(d), key_m(k) {}
  template <class T> operator T () const {
    return unarchive<T>(dict_m, key_m);
  }
};

so you can initialize with:

foo(const dictionary_t& archive, const key_type value_key) :
    value_m(wrapper(archive, value_key))
{}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top