使用指向 class 私有方法的指针的命名参数习语
Named Parameter Idiom using a pointer to a class private method
我在做一些可能不是真的事情时遇到了 C++ 编译错误 "conventional"。
为了让事情变得更简单,我只是重新编写了我试图以一种更易于阅读的方式使用的机制,并检查了我是否遇到了同样的问题。
首先是代码:
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) ) {
^
所以,我不确定这是实现我想要的目标的最佳方式(可能不是,无论如何,如果您有其他建议,我们非常欢迎),但我现在的目标是让它发挥作用或确切地理解为什么它不能工作,所以我从中学习了一些东西!
基本思路是:
- class "item" 可以使用 class "init" 的方法 "has_funcPtr" 连接到其构造函数的命名参数习惯用法进行初始化,例如: "init().has_funcPtr(&function_name)".
- class "user" 可以将指向其私有方法 "func1" 的指针存储为其类型为 "item".
的私有成员的私有成员
这样,当一个对象的特定方法"item"被调用时(为简单起见,我不在这里包含这么长的部分,因为它与错误无关,只是描述这段代码的目标)该方法可以做一些事情并通过该函数指针调用其父对象 "user" 的私有方法(我希望这足够清楚...)。
现在,我认为对象的初始化顺序存在问题,但我不确定在哪里以及如何修复它。
特别是我认为,由于 "func1" 方法不对 class "user" 的任何成员进行操作,因此可以直接在初始化列表中使用它的引用来初始化 [=47] =] 对象并将其提供给 "item" 对象。
提前谢谢大家
this->func1
不构成成员函数指针。它应该看起来像 &user::func1
如果你在 user
class.
我post这里是我问题的完整答案。我根据 Bo 的建议开发了它,并了解了如何通过指向它的指针指向特定于实例的方法。
简而言之,有两点非常重要:
指向非静态 class 成员函数的指针可以被认为只是一个偏移量而不是 "absolute address" (http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm)。这意味着如果没有实例指针,您将无法访问该函数(它只是一个偏移量)。获得实例指针后,使用此 "offset pointer" 您可以使用以下方法调用该方法:
(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)
按照第一点,如果你想让嵌套的class能够通过指向它的指针调用上层的class方法,你还需要通过upper class 访问该函数的实例指针。在我的例子中,因为我希望能够根据具体情况决定是否应该调用该方法,所以我使用了命名参数习语(下面注意 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
我在做一些可能不是真的事情时遇到了 C++ 编译错误 "conventional"。 为了让事情变得更简单,我只是重新编写了我试图以一种更易于阅读的方式使用的机制,并检查了我是否遇到了同样的问题。
首先是代码:
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) ) {
^
所以,我不确定这是实现我想要的目标的最佳方式(可能不是,无论如何,如果您有其他建议,我们非常欢迎),但我现在的目标是让它发挥作用或确切地理解为什么它不能工作,所以我从中学习了一些东西!
基本思路是:
- class "item" 可以使用 class "init" 的方法 "has_funcPtr" 连接到其构造函数的命名参数习惯用法进行初始化,例如: "init().has_funcPtr(&function_name)".
- class "user" 可以将指向其私有方法 "func1" 的指针存储为其类型为 "item". 的私有成员的私有成员
这样,当一个对象的特定方法"item"被调用时(为简单起见,我不在这里包含这么长的部分,因为它与错误无关,只是描述这段代码的目标)该方法可以做一些事情并通过该函数指针调用其父对象 "user" 的私有方法(我希望这足够清楚...)。
现在,我认为对象的初始化顺序存在问题,但我不确定在哪里以及如何修复它。 特别是我认为,由于 "func1" 方法不对 class "user" 的任何成员进行操作,因此可以直接在初始化列表中使用它的引用来初始化 [=47] =] 对象并将其提供给 "item" 对象。
提前谢谢大家
this->func1
不构成成员函数指针。它应该看起来像 &user::func1
如果你在 user
class.
我post这里是我问题的完整答案。我根据 Bo 的建议开发了它,并了解了如何通过指向它的指针指向特定于实例的方法。
简而言之,有两点非常重要:
指向非静态 class 成员函数的指针可以被认为只是一个偏移量而不是 "absolute address" (http://www.codeguru.com/cpp/cpp/article.php/c17401/C-Tutorial-PointertoMember-Function.htm)。这意味着如果没有实例指针,您将无法访问该函数(它只是一个偏移量)。获得实例指针后,使用此 "offset pointer" 您可以使用以下方法调用该方法:
(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)
按照第一点,如果你想让嵌套的class能够通过指向它的指针调用上层的class方法,你还需要通过upper class 访问该函数的实例指针。在我的例子中,因为我希望能够根据具体情况决定是否应该调用该方法,所以我使用了命名参数习语(下面注意 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