简体   繁体   English

如何将带有回调参数的系统调用包装到C ++类中?

[英]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: 因此,重要的问题是:

  1. system_call's parameter are fixed and unchangeable. system_call的参数是固定的且不可更改。
  2. the method 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: 不仅如此,使用单个指针传递任意参数很简单:

  1. 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类的指针之外,还保留所有需要的参数。

  2. new an instance of the class, put this into the pointer, and initialize the parameters to pass. new的类实例,将this放入指针,并初始化要传递的参数。

  3. Pass a pointer to this class as the arg parameter to clone() . 将指向该类的指针作为arg参数传递给clone()

  4. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM