如何将此变量简化为模板参数"在C ++中?

时间:2016-06-19 17:24:30

标签: c++ templates c++11

如何简化此代码?

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

简要陈述,

  • mfer :: tag是枚举,在名称空间mfer中定义为enum tag {};

  • mfer :: i_value是抽象类。

    class i_value {};
    
  • mfer :: t_value是模板类,

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

目前,我还不知道如何简化this make_empty_value()

理想情况下,我想这样做:

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

但我知道它是模板,所以上面的内容没有意义。

有没有想法简化这段代码? (一些现代C ++特性,Boost库等)

5 个答案:

答案 0 :(得分:2)

通过一些模板工作,我们可以将工厂功能降低到:

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

以下完整代码。

i_value生成器映射是在编译时构建的,允许进行恒定时间查找。

约束:

  • 枚举中的值必须是连续的,但它们不必从零开始。

  • 此演示需要c ++ 14。它可以很容易地适应c ++ 11。对于c ++ 03,我们希望联系到提升mpl或boost_pp。

完整的工作示例:

#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();
    }
}

预期产出:

I have tag 0
I have tag 1
tag 1 destroyed
tag 0 destroyed

答案 1 :(得分:1)

您可以将标签映射到工厂方法;

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

create_empty_value方法可能如下所示:

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

请参阅简化版 Live on Coliru

答案 2 :(得分:1)

如果枚举值遵循已知模式(默认情况下,下一个枚举值等于先前的枚举+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_);
  }

如果你不知道你的枚举的构成规则,你不能这样做,(一般的构成法就像在这个例子中,x [i + 1] = x [i] +1,或x [ i + 1] = x [i]&lt;&lt; 1(左移)。)否则他们无法迭代枚举的元素。

注意:函数compute肯定会内联,但有疑问你可以使用 编译器特定属性为__forceinline与MSVC或__attribute__((__always_inline__))与GCC或clang。

答案 3 :(得分:0)

不直接使用您的示例,但您可以在以下行中执行某些操作,即将枚举转换为类型。

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>());

答案 4 :(得分:0)

我看到的唯一简化范围是通过替换固定宏来删除样板代码。这对观众来说是安慰的。

而不是那么多if-else if,请将其设为switch/case,如下所示:

#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