Идиома Именованного Параметра с использованием указателя на закрытый метод класса

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

Прежде всего, вот код:

test.h // -- C++ --

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp;}
  init() {}
  virtual ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int (MODULE::*funcPtr)(int);
private:
  funcPtr m_fp;
public:
  item(init<MODULE> params) : m_fp(params.m_fp) {}
  virtual ~item() {}
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Method CB
  int func1(int i);
  // Item member
  item<user> m_item;
public:
  user();
  virtual ~user();
};

test.cpp // -- C++ --

#include "test.h"

user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {}

int user::func1(int i) {return 1;}

и вот ошибка:

/test.cpp:5:59: error: invalid use of non-static member function
 user::user() : m_item(init<user>().has_funcPtr(this->func1) ) {
                                                       ^

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

Основная идея заключается в том, что:

  • класс "item" может быть инициализирован с помощью именованного параметра idiom с использованием метода "has_funcPtr" класса "init", присоединенного к его конструктору, например: "init().has_funcPtr(&function_name)".
  • класс "пользователь" может хранить указатель на свой приватный метод "func1" как закрытый член своего приватного члена типа "item".

Таким образом, когда вызывается конкретный метод объекта "item" (для простоты я не включаю эту длинную часть здесь, поскольку она не имеет отношения к ошибке, а предназначена только для описания цели этого фрагмента кода) этот метод может делать вещи и вызывать закрытый метод своего родительского объекта "user" через этот указатель на функцию (надеюсь, это достаточно ясно...).

Теперь я думаю, что есть проблема с порядком инициализации объектов, но я не уверен, где и как это исправить. В частности, я подумал, что, поскольку метод "func1" не работает ни с одним членом класса "пользователь", его ссылка может быть использована непосредственно в списке инициализации для инициализации объекта "init" и передачи его элементу. объект

Спасибо всем заранее

2 ответа

Решение

this->func1 не формирует указатель на функцию-член. Должно выглядеть &user::func1 если вы в user учебный класс.

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

Короче говоря, очень важно отметить две вещи:

  1. Указатель на нестатическую функцию-член класса можно рассматривать как просто смещение, а не как "абсолютный адрес" ( http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm). Это означает, что вы не можете получить доступ к этой функции (это просто смещение) без предварительного указания указателя экземпляра. Если у вас есть указатель экземпляра, с этим "указателем смещения" вы можете вызвать этот метод, используя:

    (object_ptr->*method_ptr)(parameters_here)

    Лучше было бы использовать макрос #define, поскольку этот синтаксис действительно подвержен ошибкам и сложен для чтения ( https://isocpp.org/wiki/faq/pointers-to-members):

    #define CALL_MEMBER_FN(ptrToObject,ptrToMember) ((ptrToObject)->*(ptrToMember))

    а затем использовать его как:

    CALL_MEMBER_FN(object_ptr, method_ptr)(parameters_here)

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

Наконец, вот исправленный код, который работает (проверен):

-- C++ -- test.h

#include <iostream>

template <typename MODULE> class item;

template <typename MODULE>
class init {
public:
  typedef int  (MODULE::*funcPtr)(int);
  typedef bool (MODULE::*func2Ptr)(bool);
private:
  funcPtr  m_fp;
  func2Ptr m_fp2;
  MODULE* m_dad;
public:
  init& has_funcPtr(funcPtr fp) { m_fp = fp; return *this;}
  init& has_func2Ptr(func2Ptr fp2) { m_fp2 = fp2; return *this;}
  init(MODULE* dad) : m_dad(dad) { std::cout << "init constructor called\n"; }
  ~init() {}
private:
  friend class item<MODULE>;
};

template <typename MODULE>
class item {
public:
  typedef int  (MODULE::*funcPtr)(int);
  typedef bool (MODULE::*func2Ptr)(bool);
private:
  funcPtr  m_fp;
  func2Ptr m_fp2;
  MODULE*  m_dad;
public:
  item(init<MODULE> params) :
    m_fp(params.m_fp),
    m_fp2(params.m_fp2),
    m_dad(params.m_dad)
  {
    std::cout << "item constructor called\n";
  }
  ~item() {}
  // Method invoked externally
  int callback() {
    std::cout << "item class method callback invoked\n";
    // In the real case here do general stuff
    if(m_fp) {
      int i = (m_dad->*m_fp)(1); // call member function through its pointer
      return i;
    } else {
      std::cout << "callback not registered\n";
      return 0;
    }
  }
  // Method invoked externally
  bool callback2() {
    std::cout << "items class method callback2 invoked\n";
    // In the real case here do general stuff
    if(m_fp2) {
      bool b = (m_dad->*m_fp2)(true); // call member function through its pointer
      return b;
    } else {
      std::cout << "callback2 not registered\n";
      return false;
    }
  }
};

class user {
public:
  typedef init<user>::funcPtr funcPtr;
private:
  // Methods that optionally add more functionalities to the 2 callbacks
  int  func1(int i);
  bool func2(bool b);
public:
  // Item member
  item<user> m_item;
public:
  user();
  ~user();
};

-- C++ -- test.cpp

#include "test.h"

user::user() : m_item(init<user>(this).has_funcPtr(&user::func1) ) {
  std::cout << "user constructor called\n";
}

int user::func1(int i) {return i;}
bool user::func2(bool b) {return b;} // func2 won't be registered


int main() {
  user* u = new user();
  // Test callbacks
  int i = u->m_item.callback();
  bool b = u->m_item.callback2();
  std::cout << "main is printing i=" << i << " and b=" << b << "\n";
  std::cout << "expected results are i=1 and b=0\n" << "END\n";
  return 0;
}

ВЫХОД:

init constructor called
item constructor called
user constructor called
item class method callback invoked
items class method callback2 invoked
callback2 not registered
main is printing i=1 and b=0
expected results are i=1 and b=0
END