How can I simplify this Code?
mfer::i_value* make_empty_value(mfer::tag tag_)
{
if (tag_ == mfer::tag::mwf_ble) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ble>());
} else if (tag_ == mfer::tag::mwf_chn) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_chn>());
} else if (tag_ == mfer::tag::mwf_blk) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_blk>());
} else if (tag_ == mfer::tag::mwf_seq) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_seq>());
} else if (tag_ == mfer::tag::mwf_man) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_man>());
} else if (tag_ == mfer::tag::mwf_ivl) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ivl>());
} else if (tag_ == mfer::tag::mwf_sen) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_sen>());
} else if (tag_ == mfer::tag::mwf_wfm) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_wfm>());
} else if (tag_ == mfer::tag::mwf_pre) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pre>());
} else if (tag_ == mfer::tag::mwf_off) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_off>());
} else if (tag_ == mfer::tag::mwf_nul) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nul>());
} else if (tag_ == mfer::tag::mwf_pnt) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnt>());
} else if (tag_ == mfer::tag::mwf_nte) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nte>());
} else if (tag_ == mfer::tag::mwf_txc) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_txc>());
} else if (tag_ == mfer::tag::mwf_flt) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_flt>());
} else if (tag_ == mfer::tag::mwf_skw) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_skw>());
} else if (tag_ == mfer::tag::mwf_mss) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_mss>());
} else if (tag_ == mfer::tag::mwf_pnm) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnm>());
} else if (tag_ == mfer::tag::mwf_pid) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pid>());
}
return nullptr;
}
Briefly stating,
mfer::tag is enumeration, defined like enum tag {};
in namespace mfer.
mfer::i_value is abstract class.
class i_value {};
mfer::t_value is templated class like,
template <mfer::tag tag_type>
class t_value : public i_value {};
At this moment, I don't know how to simplify this make_empty_value()
.
Ideally, I want to make it like this:
mfer::i_value* make_empty_value(mfer::tag tag_)
{
return memory_manager::instance().add(new mfer::t_value<tag_>());
}
But I know that it is template, so above one doesn't make sense.
Is there any idea simplify this code? (Some modern C++ features, Boost libraries, and so on)
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.
Just like in case of the function arguments, template parameters can have their default values. All template parameters with a default value have to be declared at the end of the template parameter list.
Until variable templates were introduced in C++14, parametrized variables were typically implemented as either static data members of class templates or as constexpr function templates returning the desired values. Variable templates cannot be used as template template arguments .
An identifier that names a non-type template parameter of class type T denotes a static storage duration object of type const T, called a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template parameter.
Every template is parameterized by one or more template parameters, indicated in the parameter-list of the template declaration syntax: template < parameter-list > declaration Each parameter in parameter-list may be: a non-type template parameter;
Interestingly, variable templates are used in C++17 to provide helpers for each type traits with values. For instance, std::is_same will have a std::is_same_v helper that is a variable template. With that, we can simplify our traits a bit more:
With a little template work, we can get the factory function down to:
i_value* make_empty_value(tag tag_type)
{
static constexpr auto factory = make_factory(all_tags());
auto index = std::size_t(tag_type - tag::first);
if (index < tag::ntags) {
return memory_manager::instance().add(factory[index]());
}
else {
return nullptr;
}
}
Full code below.
The i_value generator map is built at compile time, allowing constant-time lookup.
constraints:
the values in the enum must be consecutive, but they need not begin at zero.
this demo requires c++14. It can be easily adapted to work with c++11. For c++03 we'd want to reach out to boost mpl or boost_pp.
complete working example:
#include <array>
#include <utility>
#include <deque>
#include <iostream>
// minimal implementation of virtual base
class i_value {
public:
virtual void prove() const = 0;
virtual ~i_value() = default;
};
// tag enum - note that we have supplied some extra introspection information
// these could just as well be constexpr integers outside the enum
enum tag
{
ble,
chn,
blk,
seq,
first = ble, // first available tag
last = seq, // last available tag
ntags = last-first // number of tags
};
/// Function to offset an index sequence by the distance from
/// zero to the first available tag - in case the first tag is not zero
template<std::size_t...tags>
constexpr auto tag_offset(std::index_sequence<tags...>)
{
return std::index_sequence<(tags + tag::first)...>();
}
/// Function to compute an index sequence of all valid tags
constexpr auto all_tags()
{
return tag_offset(std::make_index_sequence<std::size_t(ntags)>());
}
/// Factory function to generate a derived class for a given tag
template <tag tag_type>
class t_value : public i_value {
void prove() const override { void(std::cout << "I have tag " << tag_type << std::endl); }
~t_value() { void(std::cout << "tag " << tag_type << " destroyed" << std::endl); }
};
template<tag tag_type>
i_value* make_instance()
{
return new t_value<tag_type>();
}
/// Function to generate a 'factory' - an array of factory functions, one for
/// each tag in the variadic template argument tags...
/// Note that the array is zero-based, the tags may not be. All we care about
/// here is the size of the list of tags (and their values)
///
template<std::size_t...tags>
constexpr auto make_factory(std::index_sequence<tags...>)
{
return std::array<i_value* (*)(), sizeof...(tags)>
{
&make_instance<static_cast<tag>(tags)>...
};
}
// minimal memory manager
struct memory_manager {
struct impl {
i_value* add(i_value* item) {
_ivalues.push_back(item);
return item;
};
~impl() {
for (auto i = _ivalues.rbegin() ; i != _ivalues.rend() ; ++i) {
delete *i;
}
}
std::deque<i_value*> _ivalues;
};
static impl& instance()
{
static impl _instance = {};
return _instance;
}
};
// here is resulting factory function.
i_value* make_empty_value(tag tag_type)
{
static constexpr auto factory = make_factory(all_tags());
auto index = std::size_t(tag_type - tag::first);
if (index < tag::ntags) {
return memory_manager::instance().add(factory[index]());
}
else {
return nullptr;
}
}
// test
int main()
{
for(auto tag_type : { tag::ble, tag::chn })
{
auto pvalue = make_empty_value(tag_type);
pvalue->prove();
}
}
expected output:
I have tag 0
I have tag 1
tag 1 destroyed
tag 0 destroyed
You can map the tags to a factory method;
typedef std::unordered_map<mfer::tag,std::function<mfer::i_value*()>> TagMap;
TagMap create_tag_map()
{
TagMap map;
map[mfer::tag::mwf_ble] = [](){ return new mfer::t_value<mfer::tag::mwf_ble>(); };
map[mfer::tag::mwf_chn] = [](){ return new mfer::t_value<mfer::tag::mwf_chn>(); };
map[mfer::tag::mwf_blk] = [](){ return new mfer::t_value<mfer::tag::mwf_blk>(); };
//...
return map;
}
The create_empty_value
method could then look like this:
mfer::i_value* make_empty_value(mfer::tag tag_)
{
static TagMap factory = create_tag_map();
auto it = factory.find( tag_ );
if( it != factory.end() )
{
return memory_manager::instance().add( it->second() );
}
return nullptr;
}
see simplified version Live on Coliru
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With