2016-06-10 26 views
5

我试图在这里实现的情况是一个基类,它有一个函数(我们称之为modify_command),它可以虚拟地接受许多不同的类型,所以派生类可以实现他们认为合适的modify_command函数。 现在我有沿着这些线路中的基类的东西:C++设计:重载/覆盖很多很多功能,清理的方法?

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

然后在派生类中:

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

显然,一个虚拟模板函数的可能性也不大所以在某种形式的我将必须列出许多参数可能性的众多函数声明,这些可能性肯定会使类定义混乱,并且可能会使其他命令类型难于处理(随着时间的推移)

使模板编辑功能是针对这种情况,而无需modify_command(SpecificCommandC)的定义编译记录一个错误:

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 

我真的很讨厌我怎么会有这方面的工作,没有任何人有一个建议就如何能够清理/重新实现?随着软件的成熟,命令的数量将继续增长,因此可扩展性是必须的。

编辑:语法

+0

使用成员函数创建SpecificCommand基类,例如onModifyCommand,其中modify_command调用。 – kfsone

+0

@kfsone我不确定我明白你的意思。我的派生类中的每一个都会以不同的方式修改命令(有时根本不会)。这将如何让我的Derivations能够指定它自己的修改命令的实现? – mascoj

+0

看来你想要一个虚拟模板函数的等价物,而不必将派生类中的所有特定重载声明为基类中的虚函数。请确认 – EdMaster

回答

1

不幸的是,将解决你的问题是一个虚拟模板的方法,这是不可能的。

这里是一个更C-ISH解决方案带到了C++的世界,可以解决限制:

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

的基本想法是给在运行时的数值类型的命令(它可以用CRTP惯用法完成 - 参见BaseCommandCommand模板类)。
可以访问这个值,这是创建一个类型擦除处理程序来处理您想要为其提供特定实现(请参阅assignHandler/THandler)的命令的问题。
一旦你正确设置了所有的部分,你只需要在派生类中设计和初始化这些处理程序。因为它可以使用std::function完成,所以可以使用lambda,公共或私有成员方法,静态方法等作为处理程序。
有关更多详细信息,请参阅Derived的构造函数。

+0

谢谢你。我认为一个处理程序的地图可能是最好的方式来做这个没有超过30+的声明,+1 – mascoj

+0

决定采取你的榜样,并为了更好地适应我的需要而篡改了一些信息,我在一些问题上忽略了一些信息是专有的,可以轻松地为这种用法生产钥匙。再次感谢! – mascoj

+0

@mascoj不客气。 :-) – skypjack

1

我不知道这是否是你想要的,但我会提前与我已经想出:

小元程序的类型串内找到一个类型/可变参数列表。需要确定一个命令是否是实施的一部分。

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

现在定义有一个共同的基类的命令:

struct CommandBase {}; 

struct CommandA: CommandBase {}; 
struct CommandB: CommandBase {}; 
struct SomeCommandType: CommandBase {}; 
struct CommandC: CommandBase {}; 

using AvailableCommmands = meta::list<CommandA, CommandB, SomeCommandType>; 

AvailableCommmands类型决定了not implemented消息应该想出什么类型。如果AvailableCommmands meta :: list中不存在特定的命令类型,则应打印该类型的not implemeneted消息。

休息的代码(基地+衍生)的:

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

的代码是可以理解的凌乱,我相信一定有更好的方法来做到这一点。

+0

解决此问题的有趣方法。可能不适合我正在寻找的东西。我试图分开的实现,使一个派生类可能有一个处理程序的CommandA,但不是CommandB和另一个类可能有一个处理程序的CommandB而不是CommandA,而另一个可以处理A和B.我只看了这个,但似乎这个解决方案将所有派生必须处理列表中的命令? – mascoj

+0

@mascoj:你可以在'do_on_modify_command'函数内用'dynamic_cast'而不是'static_cast'来实现。但是为了这个工作,你必须通过添加至少一个虚拟函数来创建'CommandBase'多态。 – Arunmu

0

我可能还没有完全理解你的问题,但我带着一个解决方案来解决如何允许在模板中作为“虚函数”工作的派生类中重载的问题。 我做了一个小的变化:SpecificCommandASpecificCommandB有一个共同的祖先类Command,如果你的代码他们不这样做,只需创建一个Command类,并有类似struct SpecificAPrime: Common, SpecificCommandA {};

首先代码:

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

这个想法如下:因为现在命令有一个共同的祖先,并且是多态的,可以使用动态多态而不是模板,包括typeid运算符。

现在modify_command(Command &)模板,也不是多态函数,它的作用是找到命令的type_index并将其转发给一个多态函数。多态函数modify_command(Command &, type_index)的实现在内部有一个type_index映射,他们知道如何特别修改。如果给定的type_index不在映射中,则它们默认为基类实现,如果type_index位于映射中,则调用映射元素,这只是调用给定特定过载的模板函数的实例化。

从某种意义上说,这是一个多态型开关。

下面是一个完整,运行例如:http://ideone.com/K1Xmdk