有没有办法调用模板参数类的未知方法?

时间:2013-10-04 18:36:15

标签: c++ class templates

我曾经实现过这样的状态机:

class Player
{
public:
    int Run();
    int Jump();
    int Stop();

private:
    class State
    {
    public:
        virtual int Run() = 0;
        virtual int Jump() = 0;
        virtual int Stop() = 0;
    };

    class StandingState : public State
    {
        virtual int Run() { /*...*/ }
        virtual int Jump() { /*...*/ }
        virtual int Stop() { /*...*/ }
    };

    class RunningState : public State
    {
        virtual int Run() { /*...*/ }
        virtual int Jump() { /*...*/ }
        virtual int Stop() { /*...*/ }
    };

    // More states go here!

    std::list<State*> states;
    State* currentState;
};

int Player::Run()
{
    int result = m_currentState->Run();

    // do something with result
}

int Player::Jump()
{
    int result = m_currentState->Jump();

    // do something with result
}

int Player::Stop()
{
    int result = m_currentState->Stop();

    // do something with result
}

我认为应该是教科书:Player将来自外部的调用委托给当前的State对象,并对结果做一些事情(可能转换到另一个状态)。本质上,每个状态都知道给定的动作如何影响它,但由状态机决定将各种状态连接在一起。我发现这是一个很好的分离关注点。

但我在这里看到抽象的可能性。整个系统由State类的接口定义:

  1. 状态机和子状态都实现State
  2. 状态机保留指向所有可能的State和当前State
  3. 的指针
  4. 无论在状态机上调用State的任何方法,它都会被无条件地转发到当前状态。
  5. 所以,我们完全可以把它变成一个类模板,对吗?看:

    template< class StateInterface >
    class StateMachine : public StateInterface
    {
        // public methods already declared in StateInterface
    
    protected:
        std::list<StateInterface*> states;
        void AddState(StateInterface* state);
        StateInterface* currentState;
    };
    
    class PlayerStateInterface
    {
    public:
        virtual int Run() = 0;
        virtual int Jump() = 0;
        virtual int Stop() = 0;
    };
    
    class Player : public StateMachine< PlayerStateInterface >
    {
    public:    
        virtual int Run() { currentState->Run(); /* do stuff */ }
        virtual int Jump() { currentState->Jump(); /* do stuff */ }
        virtual int Stop() { currentState->Stop(); /* do stuff */ }
    };
    

    在上述几点中,这有1和2覆盖,但3呢?我仍然需要在具体的状态机实现中手动将调用委托给当前状态。有没有办法将该功能移到StateMachine模板?我可以以某种方式表达,只要在StateInterface上调用StateMachine的方法,它就应该在currentState上调用相同的方法,当我不知道StateInterface的名称或签名时方法?

1 个答案:

答案 0 :(得分:1)

如果您正在寻找RunJumpStop具有不同签名的案例的一般答案,我不知道是否有一个好的解决方案。但是,在您的示例中,它们都具有相同的签名,这表明以下方法可能有效:

#include <iostream>

class AbstractState
{
public:
    virtual void write1() = 0;
    virtual void write2() = 0;
};

class State1: public AbstractState
{
public:
    virtual void write1() { std::cout << "1-1" << std::endl; }
    virtual void write2() { std::cout << "1-2" << std::endl; }
};

class State2: public AbstractState
{
public:
    virtual void write1() { std::cout << "2-1" << std::endl; }
    virtual void write2() { std::cout << "2-2" << std::endl; }
};

template <typename StateInterface>
class Player
{
public:
    Player(StateInterface *s_):
        s(s_)
    {
    }

    void setState(StateInterface *s_)
    {
        s = s_;
    }

    void execute(void (StateInterface::*method)())
    {
        (s->*method)();
    }
private:
    StateInterface *s;
};

int main()
{
    State1 s1;
    State2 s2;

    Player<AbstractState> p(&s1);

    p.execute(&AbstractState::write1);
    p.execute(&AbstractState::write2);

    p.setState(&s2);

    p.execute(&AbstractState::write1);
    p.execute(&AbstractState::write2);

    return 0;
}

我能够使用GCC 4.5.2编译并运行它并获得预期的结果,即:

1-1
1-2
2-1
2-2

正如我所说的,我不确定是否有一种很好的方法可以将AbstractState的不同成员函数用于不同的参数或返回不同的值,并且可能存在其他缺点。尚未考虑过。它不如我认为你希望找到的那么好,但希望这至少可以作为一个很好的起点。