C ++ обратный вызов нестатического метода интерфейса - PullRequest
0 голосов
/ 03 октября 2011

У меня есть класс A, который должен вызывать нестатический метод класса из интерфейсного класса B с сигнатурой, выраженной, скажем, следующим указателем функции:

bool (B::*check)(int) const

Такой метод будет реализован набором классов {C}, реализующих B, каждый из которых имеет несколько методов, соответствующих вышеуказанной сигнатуре.Поэтому мне нужно привязать обратный вызов от A к B, который, в свою очередь, делегировал бы выбранный метод из C.

Отредактировано с помощью некоторого кода:

Этонабросок того, что я имею в виду.Помните, что это только пример требования, приведенного выше, в организации кода нет ничего обязательного вне, возможно, класса А.

class B {
  public:
    bool check(int val) const {
       // call to a binded method with a given signature (sig) on a Cx class
       ...
    }
};

class C1: public B {
  ...
  // a method implementing the (sig) signature
  // another method implementing the (sig) signature
};


class C2: public B {
  ...
  // a method implementing the (sig) signature
  // another method implementing the (sig) signature
};

class A {
  public:
  ...
  private:
    bool result;
    int val;

    void call_check(B const& b) const {
      result = b.check(val);      
    }
  ...
};

Это возможно в C ++?Или, что эквивалентно, каким было бы решение, позволяющее A знать только о классе B?

К моему недоумению, я не нашел решения для этой очень специфической потребности.

Ответы [ 4 ]

1 голос
/ 03 октября 2011

Доступны указатели на функции-члены, которые делают именно то, что вы хотите.Тем не менее, просто имейте в виду, что они на самом деле не являются «указателями», а «похожи на указатели», поскольку возможно , что правильный поиск / вызов должен проходить через таблицу (и) «vptr», связанную скласс, который может иметь более одного родителя.

Короче говоря: Да, вы можете иметь его.Если вы реализуете в шаблоне, вам не нужно включать целевые заголовки (но вам потребуется , чтобы реализовать заголовки целевого класса в точке расширения кода).Если вы реализуете как не шаблон, то вы можете объявить функции-члены и заставить их работать.Или вы можете просто включить целевые заголовки типа класса.

Поскольку доступно несколько целевых функций, да, в точке фактической привязки вы должны включить заголовок (вам не нужен заголовок, если этоявляется реализацией шаблона):

class MyA {
public:
  bool foo1(int) const;
  bool foo2(int) const;
};

void MyFunc(void) {
  bool (MyA::*my_ptr_to_func)(int) const;
  my_ptr_to_func = &MyA::foo2;

  MyA my_a;

  // call it
  if((my_a.*my_ptr_to_func)(42))
  {
    // ...
  }
}

[ОБНОВЛЕНИЕ] , основываясь на обновленном коде, кажется, что вы просто хотите сделать "bool B::check(int) const" равным "virtual"в базовом классе и переопределить / повторно реализовать эту функцию в производных классах" C1 "и" C2 "?

Да, будет вызвана функция virtual (реализация вклассы C1 и C2), хотя указатель изначально был на функцию B::check(int).Это работает, и это точно , почему функция указателя на член не является точно указателем, но похожа на указатель (чтобы разрешить ваш вызовправильно выполнить код virtual в производных классах).

Так что, не бойтесь: все будет работать, просто поместите "virtual" на B::check(int) в базе.

1 голос
/ 03 октября 2011

Массовое редактирование

Я думаю, что получил то, что вы хотите, основываясь на некотором тяжелом наборе типов.

Обратите внимание, что я делаю НЕ рекомендую эту технику.Хотя мой пример работает, я полагаю, что здесь есть некоторые большие ловушки, которые могут действительно испортить ситуацию.

Как и раньше, класс A может принимать как метод правильной сигнатуры, так и объект (типа * 1012).* или производного от типа B, такого как ваши C классы), и вызовите указанный метод для указанного объекта.

Класс B на самом деле вообще не имеет никаких методов, и толькодействует как общий базовый класс для двух классов C1 & C2.

Внизу находится main, который демонстрирует, как это используется.Я упустил реализацию двух SetCallback*** методов, поскольку они тривиальны.

class B
{   public: // Has nothing, only serves as base-class for C1 and C2.        
};

class C1: public B
{
    public: bool DoThis(int x) const { return true; }
};

class C2: public B
{
    public: bool DoThat(int x) const { return false; }
};

class A
{
    private:
        bool (B::*m_check)(int) const;
        B* m_Obj;

    public:
        void SetCallbackFunction(bool (B::*fnc)(int) const)
        {   m_check = fnc; }

        void SetCallbackObject(B* pB)
        {   m_Obj = pB;  }

        bool InvokeCallback(int val)
        {
            return (m_Obj->*m_check)(val);
        }
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a;
    C1 c;

    bool (C1::*func)(int) const;
    bool (B::*b_func)(int) const;

    func = &C1::DoThis;
    b_func = (bool (B::*)(int) const)(func);   // Dangerous Typecasting.  Use caution!

    a.SetCallbackFunction(b_func);
    a.SetCallbackObject((B*)(&c));   // A simpler, safer typecast.

    a.InvokeCallback(5);  // Ends up calling C1::DoThis.

    _getch();
    return 0;
}
1 голос
/ 03 октября 2011

Самое простое, что вы можете сделать, это не использовать указатель на функцию-член, а использовать конструкцию более высокого порядка, такую ​​как function (либо boost или C ++ 11), и зарегистрировать обратные вызовы с помощью bind (сноваboost или C ++ 11).

0 голосов
/ 03 октября 2011

Звучит так, как будто вы хотите использовать шаблон наблюдателя, чтобы позволить A содержать вектор указателей на функции типа bool (B::*check)(int) const.

Таким образом, классы в {C} могут регистрироваться через шаблон наблюдателя в A. Я не понимаю, зачем вам явно нужен интерфейс B, если вы используете эту форму шаблона. Интерфейс будет гарантирован вектором указателей на функции, требующих подписи выбранного вами указателя на функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...