C++ Design: Overloading/Overriding many many functions, way to clean up?

mascoj

The case I am trying to implement here is a Base class which has a function (let's call it modify_command) which can accept many different types virtually so derived classes can implement the modify_command function as they see fit. Right now I have something along these lines in the base class:

class Base 
{
    template<typename Command>
    void modify_command(Command cmd)
    { 
        std::cout << "Modify command called with unimplemented command type:" << typeid(cmd).name();
    }

    virtual void modify_command(SpecificCommandA cmd)
    {
         modify_command<SpecificCommandA>(cmd); // Calls the templated function
    }

    virtual void modify_command(SpecificCommandB cmd)
    {
         modify_command<SpecificCommandB>(cmd); // Calls the templated function
    }

    // etc.
};

Then in a derived class:

class Derived : public Base
{
    virtual void modify_command(SpecificCommandA cmd)
    {
        cmd.x = 1;
        cmd.y = 2;
    }
}

Obviously a virtual template function isn't a possibility so in some form I am going to have to list function declarations for umpteen many argument possibilities which is sure to clutter the class definition and may make it difficult (over time) for additional command types to be handled

The purpose of having the templated function is for this case to compile without needing definition of modify_command(SpecificCommandC) to log an error:

Base * base = new Derived();
SpecificCommandA a;
SpecificCommandB b;
SpecificCommandC c;
base->modify_command(a); //Set's x and y
base->modify_command(b); //Outputs that command type is unimplemented
base->modify_command(c); //Outputs that command type is unimplemented

I really hate how I have this working, does anyone have a suggestion on how this can be cleaned up / reimplemented? The number of commands will continue to grow as the software matures, so extensibility is a must.

Edit: Grammar

skypjack

Unfortunately, what would solve your problem is a virtual template method, that is not possible.

Here is a more C-ish solution brought to the C++ world that can work around the limitation:

#include<unordered_map>
#include<functional>
#include<memory>
#include<iostream>
#include<utility>

struct BaseCommand {
    static int counter;
};

int BaseCommand::counter = 0;

template<class T>
struct Command: BaseCommand {
    static int type() {
        static const int t = ++counter;
        return t;
    }
};

struct SpecificCommand1: Command<SpecificCommand1> {};
struct SpecificCommand2: Command<SpecificCommand2> {};

class Base {
    struct Handler {
        virtual void operator()(BaseCommand &cmd) = 0;
    };

    template<typename T>
    struct THandler: Handler {
        std::function<void(T)> func;
        void operator()(BaseCommand &cmd) override {
            func(static_cast<T&>(cmd));
        }
    };
protected:
    template<typename T>
    void assign(std::function<void(T)> f) {
        auto handler = std::make_unique<THandler<T>>();
        handler->func = f;
        handlers[T::type()] = std::move(handler);
    }

public:
    template<typename Command>
    void modifyCommand(Command cmd) {
        auto it = handlers.find(Command::type());
        if(it == handlers.end()) {
            std::cout << "Modify command called with unimplemented command type: " << Command::type();
        } else {
            auto &h = *(it->second);
            h(cmd);
        }
    }

private:
    std::unordered_map<int, std::unique_ptr<Handler>> handlers;
};

class Derived: public Base {
public:
    Derived() {
        std::function<void(SpecificCommand1)> f =
            [](SpecificCommand1) {
                std::cout << "handler for SpecificCommand1" << std::endl;
            };

        assign(f);
    }
};

int main() {
    Base *b = new Derived;
    b->modifyCommand(SpecificCommand1{});
    b->modifyCommand(SpecificCommand2{});
}

The basic idea is to give a numeric type at runtime to your commands (it can be done with the CRTP idiom - see BaseCommand and the Command template class).
With this value accessible, it's a matter of creating a type-erased handler to deal with the commands for which you want to provide a specific implementation (see assign and Handler/THandler).
Once you have all the pieces correctly set, you have only to design and initialize those handlers in your derived classes. For it can be done using std::function, you can use as an handler a lambda, a public or private member method, a static method and so on.
See the constructor of Derived for further details.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Clean way to select from many options in Python

Best way to design table with many category columns

Writing clean Julia code with many subcases without member functions

Way too many network adapters showing up

Is there any clean way to append items to a one-to-many relationship?

Rails: Many to many database design

Laravel Schema design many to many

How to stack up many vars and functions in EvalMath class (PHP)?

Is there a way to clean up this structure?

Create a way to stop to many duplicate values coming up in a JTable

any way to speed up php function? many curl requests

Efficient way to implement multiple dispatch for many similar functions

Is there any way to override standard many2many field pop up after clickin add an item?

A cleaner way to pass many parameters into many functions in a script, without moving to object-oriented code structure?

Multiprocessing "OSError: [Errno 24] Too many open files": How to clean up jobs and queues?

DB Design: additional info for many to many field

Database design for many to many relationship with optional columns?

Rails: many to many relationship join table design

Does many-to-many relationship design properly

SQL Many-to-many relation design

Many to many resource mapping restful api design

Database design for User, UserRole - many to many relationship

REST URL Design for Many to Many Relationship

AWS DynamoDB many to many schema design clarification

database design many to many and a owning reference

Is this database design many-to-many correct for production?

Struggling with many-to-many (one table) design

How to design this mapping (many:many) in MongoDB?

Database design need better solution many to many