Reputation: 35935
I have:
unarchive
that takes a dictionary and a key and based on the template type (T
) passed can be specialized to produce a T
struct
that leverages unarchive
to construct its membersAn 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?
Upvotes: 2
Views: 186
Reputation: 42584
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))
{}
Upvotes: 1
Reputation: 122001
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.
Upvotes: 4