Как определить указатель на метод, который возвращает указатель на метод? - PullRequest
9 голосов
/ 02 октября 2008

В основном у меня есть следующий класс:

class StateMachine {
...
StateMethod stateA();
StateMethod stateB();
...
};

Методы stateA () и stateB () должны иметь возможность возвращать указатели на stateA () и stateB (). Как ввести StateMethod?

Ответы [ 5 ]

14 голосов
/ 02 октября 2008

GotW # 57 говорит об использовании прокси-класса с неявным преобразованием для этой цели.

struct StateMethod;
typedef StateMethod (StateMachine:: *FuncPtr)(); 
struct StateMethod
{
  StateMethod( FuncPtr pp ) : p( pp ) { }
  operator FuncPtr() { return p; }
  FuncPtr p;
};

class StateMachine {
  StateMethod stateA();
  StateMethod stateB();
};

int main()
{
  StateMachine *fsm = new StateMachine();
  FuncPtr a = fsm->stateA();  // natural usage syntax
  return 0;
}    

StateMethod StateMachine::stateA
{
  return stateA; // natural return syntax
}

StateMethod StateMachine::stateB
{
  return stateB;
}

Это решение имеет три основных сильные стороны:

  1. Это решает проблему по мере необходимости. Более того, он безопасен и портативный.

  2. Его механизм прозрачен: вы получаете естественный синтаксис для вызывающий / пользователь и естественный синтаксис для собственная функция "return stateA;" утверждение.

  3. Вероятно, он не требует дополнительных затрат: на современных компиляторах прокси-класс со своим хранилищем и функциями, следует встроенный и оптимизировать ни к чему.

8 голосов
/ 02 октября 2008

Используя просто typedef:

class StateMachine {  

 public:  

  class StateMethod;     
  typedef StateMethod (StateMachine::*statemethod)();   

  class StateMethod {  

    statemethod   method; 
    StateMachine& obj; 

   public:  

    StateMethod(statemethod method_, StateMachine *obj_)  
      : method(method_), obj(*obj_) {} 

    StateMethod operator()() { return (obj.*(method))(); }  
  };  

  StateMethod stateA()  { return StateMethod(&StateMachine::stateA, this); }  

  StateMethod stateB()  { return StateMethod(&StateMachine::stateB, this); }  

};    
3 голосов
/ 02 октября 2008

РЕДАКТИРОВАТЬ: njsf доказал, что я не прав здесь. Возможно, вам будет проще поддерживать статическое приведение, поэтому я оставлю все остальное здесь.

Не существует «правильного» статического типа , поскольку полный тип является рекурсивным:

typedef StateMethod (StateMachine::*StateMethod)();

Лучше всего использовать typedef void (StateMachine::*StateMethod)();, а затем делать уродливые state = (StateMethod)(this->*state)();

PS: boost::function требует явного типа возврата, по крайней мере из моего чтения документов : boost::function0<ReturnType>

2 голосов
/ 02 октября 2008

Моя философия - не использовать необработанные указатели на функции-члены. Я даже не знаю, как делать то, что вы хотите, используя необработанный указатель typedef, синтаксис такой ужасный Мне нравится использовать boost :: function.

Это почти определенно неправильно:

class X
{
  public:
    typedef const boost::function0<Method> Method;

    // some kind of mutually recursive state machine
    Method stateA()
    { return boost::bind(&X::stateB, this); }
    Method stateB()
    { return boost::bind(&X::stateA, this); }
};

Эта проблема определенно намного сложнее, чем кажется на первый взгляд

0 голосов
/ 02 октября 2008

Я никогда не смогу вспомнить ужасную функцию C ++ declspec, поэтому всякий раз, когда мне нужно выяснить, например, синтаксис, описывающий функцию-член, я просто вызываю намеренную ошибку компилятора, которая обычно отображает правильный синтаксис для меня.

Так дано:

class StateMachine { 
    bool stateA(int someArg); 
};

Какой синтаксис для typeA typedef? Понятия не имею .. поэтому давайте попробуем присвоить ему что-то не связанное и посмотрим, что говорит компилятор:

char c = StateMachine::stateA

Компилятор говорит:

error: a value of type "bool (StateMachine::*)(int)" cannot be used to initialize 
       an entity of type "char" 

Вот оно: "bool (StateMachine :: *) (int)" - это наша typedef.

...