How can I simplify this variable as a template parameter in C ++?

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; } 

Stating briefly

  • mfer :: tag is an enum defined as enum tag {}; in the mfer namespace.

  • mfer :: i_value is an abstract class.

     class i_value {}; 
  • mfer :: t_value is a template class, for example

     template <mfer::tag tag_type> class t_value : public i_value {}; 

At this moment I do not know how to simplify this make_empty_value() .

Ideally, I want to do this as follows:

 mfer::i_value* make_empty_value(mfer::tag tag_) { return memory_manager::instance().add(new mfer::t_value<tag_>()); } 

But I know that this is a template, so above one does not make sense.

Are there any ideas simplifying this code? (Some modern features of C ++, Boost libraries, etc.)

+8
c ++ c ++ 11 templates
source share
5 answers

When working with a small template, we can get the factory function before:

 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 created at compile time, which allows you to search for a constant search.

:

  • the values ​​in the enumeration must be sequential, but they must not start from zero.

  • this demonstration requires C ++ 14. It can be easily adapted to work with C ++ 11. For C ++ 03 we want to achieve an increase in mpl or boost_pp.

full 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 
+2
source share

You can match the tags with the 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 may 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 of Live on Coliru

+1
source share

You can create a recursive template function if the enumeration value follows a known pattern (by default, the following enumeration value is equal to the previous enumeration +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 do not know the constitutive rule of your listing, you cannot do this (the general constitutive law is the same as in this example, x [i + 1] = x [i] +1, or x [i + 1] = x [ i] <1 (left shift).) Otherwise, they cannot iterate over enumeration elements.

Note. The compute function will certainly be built in, but in doubt you can use a compiler-specific like __forceinline with MSVC or __attribute__((__always_inline__)) with GCC or clang.

+1
source share

Use your example directly, but you can do something on the following lines, i.e. convert enumeration to 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>()); 
0
source share

The only area of ​​simplification that I see is to remove the template code by replacing it with a fixed macro. This will reassure the viewer.

Instead of so many if-else if make it switch/case , as shown 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 
0
source share

All Articles