C++ how to replace constructor switch?

后端 未结 4 2035
执念已碎
执念已碎 2021-01-23 05:07

I would like to replace big switch with something more elegant.

class Base
{
public:
  Base(void*data, int size);
  virtu         


        
相关标签:
4条回答
  • 2021-01-23 05:28

    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();
    
    0 讨论(0)
  • 2021-01-23 05:31

    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>());
    }
    
    0 讨论(0)
  • 2021-01-23 05:43

    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();
    }
    
    0 讨论(0)
  • 2021-01-23 05:51

    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.

    0 讨论(0)
提交回复
热议问题