0

I would like to wrap a Linux System Call API (clone) into a C++ class.

However, this API required a function pointer, and it's parameter list is fixed, for instance:

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.
  2. the method start() must be non-static.

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:

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?

P.S. 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):

how to pass a non static-member function as a callback?

Community
  • 1
  • 1
Changkun
  • 1,502
  • 1
  • 14
  • 29
  • 1
    What is the "Linux System Call API", in question? This has all the potential of an XY problem. Although there are several classical hacks, to implement class method callbacks, I strongly suspect an XY problem, and there's likely a more appropriate answer for this particular "Linux System Call API", in question. – Sam Varshavchik Aug 13 '16 at 17:13
  • Wow, high signal to noise ratio: 11 lines for a 1 line system call. I recommend you review the [IOCCC](http://www.ioccc.org/) rules. – Thomas Matthews Aug 13 '16 at 17:20
  • You don't need `this` when calling static function, so you don't need `std::bind` – Ivan Aksamentov - Drop Aug 13 '16 at 17:21
  • @ThomasMatthews well, you are right about this, however we are trying to use C++, so we have to warp it into an object. If we straightly use system call, then why we just change to C code in the project? – Changkun Aug 13 '16 at 17:29
  • Are you making a call to the Internal server or the Linux System Call API means to some external environment procedure call? – BHUVANESH MOHANKUMAR Aug 13 '16 at 17:30
  • @Drop the idea of using `std::bind` is trying to make `foo`'s parameter list match the `system_call` requirement. However is not working due to the type of `func` is `std::_Bind` – Changkun Aug 13 '16 at 17:32
  • 1
    Do you want to *wrap* the system call or *warp* it? Or do you want to make a system call into a call (me) back function? – Thomas Matthews Aug 13 '16 at 17:35
  • @SamVarshavchik The original requirement all list in this question. I would like to warp the `clone()` system call into one of my class. – Changkun Aug 13 '16 at 17:36
  • @OuChangkun this idea will not work of course. You need a function with exact same signature. And system calls are all C, and C doesn't have `std::bind` or lambdas. – Ivan Aksamentov - Drop Aug 13 '16 at 17:36
  • @Drop Exactly, that's why I'm turn to here for a help.. Any better solutions? – Changkun Aug 13 '16 at 17:47
  • @ThomasMatthews Have you check my code sample? Pass `this->foo` into `system_call` is my original requirement. – Changkun Aug 13 '16 at 17:51
  • I'm still having a hard time figuring out why you need a wrap a class around a single statement or function call. There are solutions for passing pointers to member functions as call-back functions. – Thomas Matthews Aug 13 '16 at 18:03
  • What exactly is this void pointer t supposed to be pointing at? A lot of c-style system calls not only take a pointer to the callback function, but also a pointer that is used as an argument for the callback function as an argument (like `system_call(callback f, void * arg)`) If that is the case, you can essentially pass the this pointer as an argument (with some wrapping and unwrapping) – MikeMB Aug 13 '16 at 18:10
  • @ThomasMatthews Passing pointer to member functions never solve this problem, `system_call` execute its function pointer. Member function must adapt `system_call`, not reverse. So, `system_call` execute `f`, `Foo`'s non static-member function `foo` need to be execute. See? Passing `this->foo` to `system_call`. – Changkun Aug 13 '16 at 18:21

2 Answers2

1

I knew it was an XY problem. I asked specifically which system call in question, and you write that it is clone().

If you review the documentation of clone() you will find that it takes an arg parameter, also:

When the fn(arg) function application returns, the child process termi‐ nates. The integer returned by fn is the exit code for the child process. The child process may also terminate explicitly by calling exit(2) or after receiving a fatal signal.

So, all that's needed is to pass the static callback() function to clone().

Then pass the this pointer for arg.

Then have your callback() invoke the real class method.

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.

  2. new an instance of the class, put this into the pointer, and initialize the parameters to pass.

  3. Pass a pointer to this class as the arg parameter to 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 newed parameter 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.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Thank you very much Sam! I leaned a lot through you answer. I was thinking about the args in clone() is an output parameter and useless, I will learn more about C. – Changkun Aug 13 '16 at 19:11
-1

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

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):

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

Instead of a global variable and a non-member function, use a static member variable and a static member function. 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.

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
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    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. – Sam Varshavchik Aug 13 '16 at 17:22
  • Thanks for your solution! It did solved this problem, however, it not quite elegant. Global variables, which is belongs to C, you know what I mean... – Changkun Aug 13 '16 at 17:42
  • 1
    @OuChangkun Note that technically, in the last example, those are still global function and global variable, but with "elegant" `Foo::` qualifier. And what is not elegant is that now you need to expose all this elegance in the header file (Foo declaration). While if you would go with globals, you could keep them hidden in the .cpp file, so a client wouldn't get a clue about the black magic inside. – Ivan Aksamentov - Drop Aug 13 '16 at 17:58
  • @RSahu Thanks for your updates! Solution 2 makes me feel better, indeed. :) It is an excellent alternative method to avoid function pointer casting. – Changkun Aug 13 '16 at 18:10