C ++ Design: overloading / overriding many functions, a way to clean it up?

The case I'm trying to implement here is a base class that has a function (let it call modify_command) that can take many different types, 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 the derived class:

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

Obviously, the function of the virtual template is not such an opportunity, so in some form I will have to list the declarations of functions for the many possibilities of the arguments, which necessarily clutter the definition of the class and can complicate (over time) additional types of commands that need to be processed

The purpose of the template function for this case is to compile without the need to use the modify_command (SpecificCommandC) command to register an error:

 Base * base = new Derived(); SpecificCommandA a; SpecificCommandB b; SpecificCommandC c; base->modify_command(a); //Set 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 it works for me, does anyone have a suggestion on how this can be cleared or redefined? The number of teams will continue to grow as the software matures, so expansion should be mandatory.

Edit: grammar

+5
source share
3 answers

Unfortunately, the solution to this problem is the virtual template method, which is impossible.

Here is a more C-ish solution that appeared in the C ++ world, which can circumvent 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 main idea is to give the numeric type at run time your commands (this can be done using the CRTP idiom - see BaseCommand and the Command template class).
Having the available value, you need to create a handler that erases the type to process the commands for which you want to provide a specific implementation (see assign and Handler / THandler ).
After you have correctly configured all the parts, you only need to design and initialize these handlers in your derived classes. You can use std::function , you can use a lambda, a public or private member method, a static method, etc. as a handler.
See Derived Constructor for more details.

+1
source

I'm not sure if this is what you want, but I will continue what I came up with:

A small metaprogram to find the type in the list of types / variables list. It is necessary to determine whether the team is part of the implementation or not.

 namespace meta { template <typename... T> struct list{}; template <typename F, typename T> struct has_type; template <typename F> struct has_type<F, list<>> { using type = typename std::false_type; static constexpr bool value = false; }; template <typename F, typename... T> struct has_type<F, list<F, T...>> { using type = typename std::true_type; static constexpr bool value = true; }; template <typename F, typename H, typename... T> struct has_type<F, list<H,T...>> { using type = typename std::false_type; static constexpr bool value = std::is_same<F, typename std::decay<H>::type>::value ? true : has_type<F, list<T...>>::value; }; } 

Now define your teams having a common base class:

 struct CommandBase {}; struct CommandA: CommandBase {}; struct CommandB: CommandBase {}; struct SomeCommandType: CommandBase {}; struct CommandC: CommandBase {}; using AvailableCommmands = meta::list<CommandA, CommandB, SomeCommandType>; 

The AvailableCommmands type determines for which type the not implemented message should appear. If a particular coomand type is not available in AvailableCommmands meta :: list, then a message should be printed for this type not implemeneted .

The rest of the code (base + derivative):

 class Base { public: template <typename T> void on_modify_command(T cmd) { do_on_modify_command(cmd, typename meta::has_type<T, AvailableCommmands>::type()); } private: virtual void do_on_modify_command(CommandBase&, std::true_type) = 0; virtual void do_on_modify_command(CommandBase& b, std::false_type) { std::cout << "Not implemented" << std::endl; } }; class Derived: public Base { public: void do_on_modify_command(CommandBase& cmd, std::true_type) { std::cout << "Specialized command implementation" << std::endl; impl(*static_cast<SomeCommandType*>(&cmd)); } void impl(SomeCommandType cmd) { std::cout << "huh!!" << std::endl; } }; int main() { CommandA ca; Base* b = new Derived; b->on_modify_command(ca); CommandC cc; b->on_modify_command(cc); return 0; } 

The code is messy, I'm sure there must be some better way to do this.

+1
source

Perhaps I did not understand your question exactly, but I came up with a solution to the question of how to allow overloading in derived classes that will work as "virtual functions" for the template. I made a small change: SpecificCommandA and SpecificCommandB have a common Command ancestor class, if they are not in the code, just create a Command class and get something like struct SpecificAPrime: Common, SpecificCommandA {};

First code:

 #include <typeindex> #include <unordered_map> struct Command { virtual ~Command() {}; }; struct SpecificCommandA: Command {}; struct SpecificCommandB: Command {}; struct SpecificCommandC: Command {}; struct Base { virtual ~Base() {}; virtual void modify_command(Command &c, std::type_index); // default void modify_command(Command &c) { modify_command(c, std::type_index(typeid(c))); } }; template<typename Der, typename SpecC> struct Modifier { static void execute(Der &d, Command &c) { d.modify_command(static_cast<SpecC &>(c)); } }; struct Derived: Base { using Base::modify_command; // <- Because there are overloads virtual void modify_command(SpecificCommandB &); // Put here the specific code you want for SpecificCommandB. // Note that this can be overriden in derived classes void modify_command(Command &c, std::type_index ti) override { using callable = void (*)(Derived &, Command &); const static std::unordered_map<std::type_index, callable> derived_map = { { std::type_index(typeid(SpecificCommandB)), Modifier<Derived, SpecificCommandB>::execute } }; auto find_result = derived_map.find(ti); if(derived_map.end() == find_result) { Base::modify_command(c, ti); } else { (*find_result->second)(*this, c); } } }; 

The idea is this: now the commands have a common ancestor and can be polymorphic, dynamic polymorphism can be used instead of templates, including the typeid operator.

Now modify_command(Command &) is not a template or polymorphic function, what it does is find the type_index command and redirect it to a polymorphic function. Implementations of the polymorphic functions modify_command(Command &, type_index) have an internal map of type_index, which they know how to specifically modify. If this type_index not on the map, they use the base class implementation by default, and if type_index is on the map, call the displayed element, which is just an instance of the template function that causes this specific overload.

In a sense, what it does is a polymorphic switch.

Here is a complete example: http://ideone.com/K1Xmdk

0
source

All Articles