Как назначить разные указатели на функции-члены различным экземплярам регистрации классов? - PullRequest
0 голосов
/ 23 мая 2018

РЕДАКТИРОВАТЬ: я ограничен C ++ 03 по этой теме.

В следующем коде класс Impl происходит от Intf и содержит экземпляр класса Caller.

Ctor

Caller принимает экземпляр Intf:: и указатель на функцию-член;он вызывает последний для первого в Caller::func().

Impl ctor регистрирует себя и свою функцию-член func() со своим экземпляром Caller.

Я хотел бычтобы Impl содержал несколько Caller экземпляров и регистрировал разные указатели на функции-члены с каждым экземпляром, чтобы при вызове каждого содержащегося Caller экземпляра ::func() получалась другая вызываемая функция-член Impl - это можно сделать?1023 *

Единственный способ сделать это - определить несколько чисто виртуальных функций в Intf, реализовать их в Impl и зарегистрировать эти переопределяющие функции.Но это не идеальное решение для меня: я хотел бы знать, есть ли способ зарегистрировать разные указатели на функции-члены с разными экземплярами регистрирующего класса без создания виртуальных функций в классе интерфейса 1: 1 с переопределением функций в реализующем классе.

Мне нужно исключить возможность Caller взятия ссылки Impl и функции-члена;Caller может знать только о Intf с, а не Impl с.

// main.cpp
#include <iostream>

class Intf
{
public:

  virtual void func() = 0;

  typedef void (Intf::*funcPtr)();
};

class Caller
{
public:

  Caller( Intf& f, Intf::funcPtr func ) : f_( f ), func_( func ) {}

  void func() { f_.func(); }

private:

  Intf& f_;
  Intf::funcPtr func_;
};

class Impl : public Intf
{
public:

  Impl()
    : c_ ( *this, &Intf::func )
//    , c2_( *this, static_cast<Intf::funcPtr>(func2) )
  {
  }

  void callCaller() { c_.func(); };

//  void callCaller2() { c2_.func(); };

private:

  void func()
  {
    std::cout << __FUNCTION__ << std::endl;
  }

  void func2()
  {
    std::cout << __FUNCTION__ << std::endl;
  }

  Caller c_;
//  Caller c2_;
};

int main( int argc, char* argv[] )
{
  Impl i;
  i.callCaller();
  return 0;
}

Дополнительный вопрос: кто-то может объяснить, почему в ctor Impl необходимо указывать указательдля функции-члена func() с Intf::?Т.е. почему это правильно ...

Impl() : c_ ( *this, &Intf::func ) {}

... и почему это не правильно ...

Impl() : c_ ( *this, &Impl::func ) {}
Impl() : c_ ( *this, &func ) {}

?

Ошибка компилятора вПример версии & & Impl :: func:

g ++ -g main.cpp && ./a.out

main.cpp: In constructor 'Impl::Impl()':
main.cpp:31:31: error: no matching function for call to 'Caller::Caller(Impl&, void (Impl::*)())'
     : c_ ( *this, &Impl::func )
                               ^
main.cpp:31:31: note: candidates are:
main.cpp:16:3: note: Caller::Caller(Intf&, Intf::funcPtr)
   Caller( Intf& f, Intf::funcPtr func ) : f_( f ), func_( func ) {}
   ^
main.cpp:16:3: note:   no known conversion for argument 2 from 'void (Impl::*)()' to 'Intf::funcPtr {aka void (Intf::*)()}'
main.cpp:12:7: note: Caller::Caller(const Caller&)
 class Caller
       ^
main.cpp:12:7: note:   candidate expects 1 argument, 2 provided

Ошибка компилятора ввариант версии & & func:

>g++ -g main.cpp && ./a.out
main.cpp: In constructor 'Impl::Impl()':
main.cpp:31:20: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say '&Impl::func' [-fpermissive]
     : c_ ( *this, &func )
                    ^
main.cpp:31:25: error: no matching function for call to 'Caller::Caller(Impl&, void (Impl::*)())'
     : c_ ( *this, &func )
                         ^
main.cpp:31:25: note: candidates are:
main.cpp:16:3: note: Caller::Caller(Intf&, Intf::funcPtr)
   Caller( Intf& f, Intf::funcPtr func ) : f_( f ), func_( func ) {}
   ^
main.cpp:16:3: note:   no known conversion for argument 2 from 'void (Impl::*)()' to 'Intf::funcPtr {aka void (Intf::*)()}'
main.cpp:12:7: note: Caller::Caller(const Caller&)
 class Caller
       ^
main.cpp:12:7: note:   candidate expects 1 argument, 2 provided

1 Ответ

0 голосов
/ 23 мая 2018

Вы можете использовать старый способ обратного вызова:

typedef void (*callback_t)(void*);

вспомогательная функция:

template <typename C, void (C::*M)()>
void member_func(void* instance)
{
    C* c = static_cast<C*>(instance); // Not typesafe :-/
    ((*c).*M)();
}

А затем

class Caller
{
public:
    Caller(Intf& f, callback_t func) : instance(f), func_(func) {}

    void func() const { func_(&instance); }
private:
    Intf& instance;
    callback_t func_;
};

class Impl : public Intf
{
public:
    Impl()
      : c_  ( *this, &member_func<Intf, &Intf::func> )
      , c2_ ( *this, &member_func<Impl, &Impl::func2> )
  {
  }
// ...
};

Демо

Начиная с C ++ 11, вы можете заменить Caller на std::function<void(void)> и инициализировать его как c2_([=](){ this->func2();}).и назовите это c2_();

...