Идиома Именованного Параметра с использованием указателя на закрытый метод класса
Я застрял с ошибкой компиляции 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
учебный класс.
Я публикую здесь полный ответ на мою проблему. Я разработал его по предложению Бо и после того, как понял, как указать на конкретный метод экземпляра через указатель на него.
Короче говоря, очень важно отметить две вещи:
Указатель на нестатическую функцию-член класса можно рассматривать как просто смещение, а не как "абсолютный адрес" ( 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)
После первого пункта, если вы хотите, чтобы вложенный класс мог вызывать метод верхнего класса по указателю на него, вам также нужно передать указатель экземпляра верхнего класса для доступа к этой функции. В моем случае, так как я хотел иметь возможность решать в каждом конкретном случае, должен ли этот метод вызываться или нет, я использовал Идиому Именованных Параметров (ниже обратите внимание, что 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