【问题标题】:Is there any way to call unknown methods of a template argument class?有没有办法调用模板参数类的未知方法?
【发布时间】:2013-10-04 18:36:14
【问题描述】:

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

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. 状态机保存指向所有可能的States 和当前State 的指针
  3. 无论在状态机上调用State 的任何方法,它都会被无差别地转发到当前状态。

所以,我们完全可以把它变成一个类模板,对吧?看:

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 上调用StateInterface 的方法时,它应该在currentState 上调用相同的方法?

【问题讨论】:

  • 您可能有兴趣查看STTCL- 我正是使用该原理设计的。 (抱歉有些缺点:接口方法(事件),需要顶层和子状态机都知道)
  • 我猜StandingStateRunningState 派生自第一个代码块中的State
  • @DyP:晕!很对,已编辑。谢谢。
  • 目前还不清楚您到底要做什么。假装编译器可以理解任何人类程序员可以理解的任何东西,任何你想要的奇妙的语言结构(显然不是“为我解决这个问题”那种)。你的代码会是什么样子呢?请比“do stuff”更具体,因为不清楚“stuff”应该相同还是不同。
  • @n.m. “做事”部分并不重要。目前,我可以将具体的StateMachine 类转发到其状态对象的唯一方法是让它实现StateInterface 的所有方法。我在问是否有办法让StateMachine 模板本身进行呼叫转移。基本上,我想告诉编译器将StateMachine 的每个方法M 定义为M() { currentState-&gt;M(); }。或者至少是StateInterface中定义的那些方法。

标签: c++ class templates


【解决方案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 的不同成员函数采用不同参数或返回不同值的情况,并且可能还有其他我没有的缺点t 考虑过。它不如我认为您希望找到的那么好,但希望这至少可以作为一个好的起点。

【讨论】:

    猜你喜欢
    • 2010-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多