我提供了我正在处理的问题的简单版本。
我有一个类Entity
,它有一个map
对象的user
。为简单起见,我仅添加了一个元素(name
)。我已经在getter
对象周围实现了setter
和userMap
。
我需要wrapper
函数,以处理由于多线程情况导致的紧急情况。 (此处未显示)。我通过一些SO线程以某种方式实现了一个有效的示例。
但是正如您所看到的,我必须在(id,arg)
函数中使用wrapper
函数的同时在三个位置编写procedure
元组参数。有没有一种方法可以通过对函数参数使用某种占位符来实现相同的功能,以便调用包装器看起来很干净,即wrapper(1,static_cast<void*>(&name),function_ptr)
。
#include <iostream>
#include <functional>
#include <map>
using namespace std;
class Entity {
public:
struct user {
int id;
string name;
};
map<int,user> userMap;
void getUserName(int id,void * arg);
void setUserName(int id,void * arg);
void procedure();
void wrapper(int id,void*,std::function<void(int,void*)>);
};
void Entity::getUserName(int id,void *arg) {
auto iter = userMap.find(id);
if(iter != userMap.end()) {
*static_cast<string*>(arg) = iter->second.name;
}
}
void Entity::setUserName(int id,void *arg) {
auto iter = userMap.find(id);
if(iter != userMap.end()) {
iter->second.name = *static_cast<string*>(arg);
}
}
void Entity::wrapper(int id,void *arg,std::function<void(int,void*)> func) {
cout <<"\nSome Other critical task based on id"<< endl;
// then call the callback
func(id,arg);
}
void Entity::procedure() {
cout <<"Procedure starts"<<endl;
user u = {};
u.id = 1;
u.name = "abc";
this->userMap[1] = u;
string name;
wrapper(1,static_cast<void*>(&name),[this](int id,void*arg){
getUserName(id,arg);
});
cout <<"name :"<< name << endl;
cout <<"\nSome processing..."<<endl;
name = "def";
wrapper(1,static_cast<void*>(&name),[this](int id,void*arg){
setUserName(id,arg);
});
cout <<"\nSome more processing..."<<endl;
wrapper(1,static_cast<void*>(&name),[this](int id,void*arg){
getUserName(id,arg);
});
cout <<"name :"<< name << endl;
cout <<"Procedure ends"<<endl;
}
int main() {
Entity E;
E.procedure();
}
输出:
Some Other critical task based on id
name :abc
Some processing...
Some Other critical task based on id
Some more processing...
Some Other critical task based on id
name :def
Procedure ends
谢谢!
答案 0 :(得分:1)
我将从一个直接的答案开始,但是请继续阅读紧随其后的警告。
一种更简单的语法可以将第三个参数的类型更改为wrapper()
。不用将成员函数转换为非成员函数,只需将成员函数作为参数即可。
void wrapper(int id, void* arg, void (Entity::* func)(int,void*))
{
cout <<"\nSome Other critical task based on id"<< endl;
(this->*func)(id, arg);
}
然后您的通话将变得更简单:
wrapper(1, static_cast<void*>(&name), &Entity::getUserName);
wrapper(1, static_cast<void*>(&name), &Entity::setUserName);
但是,这真的是您要采用的方法吗?您依靠的是人们记得要调用包装器,而不是直接调用其他成员函数。如果您忘记在某个时候使用包装器,是否可以根据症状查找原因?你甚至想尝试吗?
一种更可靠的方法是将关键任务放在自己的函数中,然后让getter和setter调用它。更像是:
void criticalStuff(int id)
{
cout <<"Some critical task based on id\n";
}
void Entity::getUserName(int id, string & arg)
{
criticalStuff(id); // Critical stuff first.
auto iter = userMap.find(id);
if(iter != userMap.end()) {
arg = iter->second.name;
}
}
void Entity::setUserName(int id, const string & arg)
{
criticalStuff(id); // Critical stuff first.
auto iter = userMap.find(id);
if(iter != userMap.end()) {
iter->second.name = arg;
}
}
添加新字段的任何人在编写新的getter和setter时仍必须记住关键内容,但更常见的情况(调用这些函数)将自动处理关键内容。另外,由于似乎不再有理由将参数投射到void *
,因此您可以带回参数的类型检查。
答案 1 :(得分:0)
我认为您通过强制使用lambda
导致了自己的问题,我知道它们很有趣,但是通常不是很有用。通常,将其简单地保持为愚蠢(KISS),在您的情况下,只需将功能分为两个单独的功能即可。
class Entity {
public:
...
void getUserName(int id,void * arg);
void setUserName(int id,void * arg);
void procedure();
void criticalTask(int id); // critical task, based on id
};
void Entity::getUserName(int id,void *arg) {...}
void Entity::setUserName(int id,void *arg) {...}
void Entity::criticalTask(int id) {
cout <<"\nSome Other critical task based on id"<< endl;
}
void Entity::procedure() {
cout <<"Procedure starts"<<endl;
...
criticalTask(id);
getUserName(id, &name);
...
}
int main() {
Entity E;
E.procedure();
}