[英]How to wrap system call with callback parameter into a C++ class?
I would like to wrap a Linux System Call API (clone) into a C++ class. 我想将Linux系统调用API(克隆)包装到C ++类中。
However, this API required a function pointer, and it's parameter list is fixed, for instance: 但是,此API需要一个函数指针,并且它的参数列表是固定的,例如:
typedef int (*callback)(void *);
void system_call(callback f) {
void *t = nullptr;
f(t);
}
Now, My class like: 现在,我的课像:
class Foo {
public:
void start() {
// WRONG: cannot pass non-static member function due to pointer `this`
system_call(this->foo);
}
private:
int foo(void *args) {
f->necessary();
return 0;
}
void necessary() {
std::cout << "call success!!!" << std::endl;
}
};
int main() {
Foo obj;
obj.start();
}
So, the important problems are: 因此,重要的问题是:
start()
must be non-static. 方法start()
必须是非静态的。 I was thinking about this, by using a static member: 我正在考虑使用静态成员:
class Foo {
public:
void start() {
auto func = std::bind(foo, std::placeholders::_1, this);
system_call(func); // WRONG: cannot convert std::_Bind to a function pointer
}
private:
static int foo(void *args, Foo *f) {
f->necessary();
return 0;
}
void necessary() {
std::cout << "call success!!!" << std::endl;
}
};
or this, by using lambda with captures: 或者通过将lambda与捕获一起使用:
class Foo {
public:
void start() {
auto func = [this](void *args) -> int {
this->necessary();
};
system_call(func); // WRONG: cannot convert a lambda with captures to a function pointer
}
private:
void necessary() {
std::cout << "call success!!!" << std::endl;
}
};
They all wrong. 他们都错了。
Any solutions to fix this problem? 有解决此问题的解决方案吗?
PS I think this is a huge requirements for encapsulation, but here I found some answers are not elegant (they modified the parameter list, not possible for system call): PS:我认为这是封装的巨大要求,但在这里我发现一些答案并不完美(它们修改了参数列表,无法用于系统调用):
how to pass a non static-member function as a callback? 如何通过一个非静态成员函数作为回调?
I knew it was an XY problem. 我知道这是XY问题。 I asked specifically which system call in question, and you write that it is clone()
. 我专门询问了有问题的哪个系统调用,您写道它是clone()
。
If you review the documentation of clone()
you will find that it takes an arg
parameter, also: 如果您查看clone()
的文档,您会发现它带有arg
参数,并且:
When the fn(arg) function application returns, the child process termi‐ nates. 当fn(arg)函数应用程序返回时,子进程终止。 The integer returned by fn is the exit code for the child process. fn返回的整数是子进程的退出代码。 The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal. 子进程也可以通过调用exit(2)或在接收到致命信号后显式终止。
So, all that's needed is to pass the static callback()
function to clone()
. 因此,所需要做的就是将静态callback()
函数传递给clone()
。
Then pass the this
pointer for arg
. 然后将this
指针传递给arg
。
Then have your callback()
invoke the real class method. 然后让您的callback()
调用真实的类方法。
void callback(void *arg)
{
reinterpret_cast<Foo *>(arg)->foo();
}
Not only that, it is trivial to pass arbitrary parameters, using the single pointer: 不仅如此,使用单个指针传递任意参数很简单:
Define a struct
that holds any needed parameters in addition to the pointer to the Foo
class whose method is to be invoked. 定义一个struct
,该struct
除了指向要调用其方法的Foo
类的指针之外,还保留所有需要的参数。
new
an instance of the class, put this
into the pointer, and initialize the parameters to pass. new
的类实例,将this
放入指针,并初始化要传递的参数。
Pass a pointer to this class as the arg
parameter to clone()
. 将指向该类的指针作为arg
参数传递给clone()
。
Have your callback fetch the Foo
pointer, and all the parameters from the struct
, invoke the class method, passing to this method any needed parameters, then delete
the new
ed parameter struct. 让您的回调获取Foo
指针,然后从struct
获取所有参数,调用类方法,将所有需要的参数传递给此方法,然后delete
new
ed参数struct。
This is a very common design pattern: a function that takes a callback function pointer will also take an additional, second opaque pointer that gets forwarded to the callback-ed function, which it can use to store whatever ancillary data the callback needs. 这是一种非常常见的设计模式:带有回调函数指针的函数也将带有附加的第二个不透明指针,该指针将转发给回调函数,该指针可用于存储回调所需的任何辅助数据。 Such as a pointer to an instance of a class whose method should be invoked. 例如指向应调用其方法的类的实例的指针。 Not structuring a callback mechanism this way is actually a poor programming practice. 不以这种方式构造回调机制实际上是一种不良的编程习惯。
Solution 1 解决方案1
The simplest work around is to use a non-member function that uses a global variable. 最简单的解决方法是使用使用全局变量的非成员函数。
// The global variable
Foo* currentFooPtr = NULL;
// The callback function. Define it after the definition of Foo
int foo_callback(void *);
And then use foo_callback
as argument to system_call
after setting the value of currentFooPtr
. 然后,在设置currentFooPtr
的值之后,使用foo_callback
作为system_call
参数。
class Foo {
public:
void start() {
currentFooPtr = this;
system_call(foo_callback);
}
};
int foo_callback(void *)
{
// Use currentFooPtr any way you wish to
}
Important note for multi-threaded programs (thanks due to @SamVarshavchik): 多线程程序的重要说明(感谢@SamVarshavchik):
This'll work only as long as the application is not multithreaded; 只要应用程序不是多线程的,这将起作用。 or, specifically, it is not possible to execute the system call more than once, concurrently. 或者,特别是不可能同时执行一次以上的系统调用。 Otherwise, you'll probably want the call protected by a global mutex. 否则,您可能希望该呼叫受到全局互斥锁的保护。
Solution 2 解决方案2
Instead of a global variable and a non-member function, use a static
member variable and a static
member function. 代替全局变量和非成员函数,请使用static
成员变量和static
成员函数。 I think it is better than Solution 1 since it doesn't add a variable or a function to the global scope that are only meant to be used by Foo
. 我认为它比解决方案1更好,因为它没有向全局范围添加仅由Foo
使用的变量或函数。
class Foo {
public:
void start() {
currentFooPtr = this;
system_call(foo_callback);
}
static Foo* currentFooPtr;
static int foo_callback(void*);
};
Foo* Foo::currentFooPtr = NULL;
int Foo::foo_callback(void *)
{
// Use currentFooPtr any way you wish to
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.