Reputation: 115
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)
Upvotes: 8
Views: 312
Reputation: 69922
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
Upvotes: 2
Reputation: 70094
The only scope of simplification I see is in removing the boilerplate code by replacing with a fixed macro. This will be soothing to the viewer.
Instead of so many if-else if
, make it a switch/case
as below:
#define CASE(TAG) \
case TAG: return memory_manager::instance().add(new mfer::t_value<TAG>())
mfer::i_value* make_empty_value(const mfer::tag tag_)
{
switch(tag_) {
{
CASE(mfer::tag::mwf_ble);
CASE(mfer::tag::mwf_chn);
CASE(mfer::tag::mwf_blk);
//...
default: break;
}
return nullptr;
}
#undef CASE
Upvotes: 0
Reputation: 18081
You can create a recursive template function if the enumerate value follows a known pattern (by default next enumerate value equals previous enumerate +1):
//anonymous namespace to "help innliner"
namespace{
//This function return the next enumerates value:
constexpr mref::tag next_tag(mref::tag tag_) {
return static_cast<mref::tag>(
static_cast<std::underlying_type_t<mref::tag>>(tag_) + 1);
}
//The compute function is wrapped in a structure to enable template
//specialization:
template <mref::tag Tag> struct add_to_mem_manager {
static mfer::i_value* compute(mref::tag tag_) {
if (Tag == tag_) {
return memory_manager::instance().add(
new mfer::t_value<Tag>());
} else {
return add_to_mem_manager<next_tag(Tag)>::compute(tag_);
}
}
};
//Specialization for last enumerate
template <> struct add_to_mem_manager<mfer::tag::mwf_pid> {
static mref::ivalue* compute(mref::tag tag_) {
assert(mref::tag::mwf_pid == tag_);
return memory_manager::instance().add(
new mfer::t_value<mfer::tag::mwf_pid>());
}
};
}
mfer::i_value* make_empty_value(mfer::tag tag_){
//call with template parameter equals to the
//the enumerate whose values is the smallest
return add_to_mem_manager<mfer::tag::mwf_ble>::compute(tag_);
}
If you don't know the constitutive rule of your enumerate, you cannot do this,( generaly constitutive law is as in this example, x[i+1]=x[i]+1, or x[i+1]=x[i]<<1 (left shift).) Otherwise their is no way to iterate over elements of an enumeration.
Note: The function compute
will certainly be inlined, but in doubt you can use
compiler specific attribute as __forceinline
with MSVC or __attribute__((__always_inline__))
with GCC or clang.
Upvotes: 1
Reputation: 5148
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
Upvotes: 1
Reputation: 6901
Not directly using your example, but you can do something on the below lines, i.e converting enum to a type.
enum Type {
Type_A,
Type_B,
};
template <Type T>
struct Enum2Type {
constexpr static const Type type = T;
};
template <typename T>
mfer::i_value* make_empty_value(T tag_type)
{
return memory_manager::instance().add(new mfer::t_value<tag_type.type>());
}
auto val = make_empty_value(Enum2Type<Type_A>());
auto val2 = make_empty_value(Enum2Type<Type_B>());
Upvotes: 0