C++ how to replace constructor switch?

给你一囗甜甜゛ 提交于 2019-12-01 23:39:17

Find an implementation of static_for, and it's simplicity itself:

using list = std::tuple<A, B, C, D, E, F, G, ...>;

const auto n = c - 'a';
static_for<std::tuple_size<list>()>([&](auto N){
    if (n != N)
        return;
    using T = std::tuple_element_t<list, N>;
    T obj(data, size);
    obj.Something();
});

Further considerations:

  1. If they all have the same polymorphic interface, you could decide to only use this for creating the object.

  2. If you have holes in your range, if constexpr and std::is_same are your friends.

  3. It might be better to use some dedicated typelist-type rather than std::tuple, but this works in a pinch.

An unpolished, quick and dirty example-implementation for static_for():

template <std::size_t Is, class F>
void static_for_impl(F&& f, std::index_sequence<Is...>) {
    f(std::integral_constant<std::size_t, Is>()), ...;
}

template <std::size_t N, class F>
void static_for(F&& f) {
    static_for_impl(f, std::make_index_sequence<N>());
}

If the constructors are exactly the same and Something methods are called similarly then you should be able to use templates like this:

template<typename T>
void DoSomething(void*data, int size){
    T t(data, size);
    t.Something();
}
..
{
    switch(input){
        case 'a': DoSomething<A>(..); break;
        case 'b': DoSomething<B>(..); break;
    }
}

you can use is_base_of if you want to validate that the template is a derived class of Base.

Since you switch on an unknown variable (in this case a char) I'm not sure how you would minimize the switch, unless following the pattern suggested by Alan Birtles.

Something like this should work:

#include <map>
#include <functional>
#include <memory>

typedef std::function< std::unique_ptr< Base >( void* data, int size ) > Factory;
std::map< char, Factory > factories =
{
    { 'a', []( void* data, int size ){ return std::make_unique<A>( data, size ); } },
    { 'b', []( void* data, int size ){ return std::make_unique<B>( data, size ); } }
};
char input = 'a';
void* data = 0;
int size = 0;
auto factory = factories.find( input );
if ( factory != factories.end() )
{
    factory->second( data, size )->Something();
}

You just need to add a single line to the factories list for each class.

If you are using an enum with contiguous values starting from 0 then you can just use an array rather than a std::map, e.g:

enum class Class
{
    a,
    b
};

Factory factories[] =
{
    []( void* data, int size ){ return std::make_unique<A>( data, size ); },
    []( void* data, int size ){ return std::make_unique<B>( data, size ); }
};
Class input = Class::a;
factories[static_cast<size_t>(input)]( data, size )->Something();

You can use a simple factory method to create objects by required type and constructor parameters as in following example. Don't forget the virtual destructor when using inheritance and virtual functions.

#include <memory>

class Base
{
public:
    Base(void* data, int size) {};
    virtual ~Base() {}
    virtual void Something() = 0;
};

class A : public Base
{
public:
    A(void* data, int size) : Base(data, size) {}
    void Something() override {};
};

class B : public Base
{
public:
    B(void* data, int size) : Base(data, size) {}
    void Something() override {};
};

Base* MyFactory(char type, void* data, int size)
{
    switch (type)
    {
        case 'a': return new A(data, size);
        case 'b': return new B(data, size);
        default:
            return nullptr;
    }
}

int main()
{
    std::unique_ptr<Base> obj1(MyFactory('a', nullptr, 1));
    obj1->Something();
    std::unique_ptr<Base> obj2(MyFactory('b', nullptr, 1));
    obj2->Something();
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!