0

I am using a C library that implements a command shell. Custom shell commands are registered by implementing a function with the following call signature:

typedef void(* shellcmd_t)(BaseSequentialStream *chp, int argc, char *argv[])

And then registering that function in a static structure which maps a string command name to that function pointer as follows:

static const ShellCommand commands[] = {
  {"myfunction", myfunction},
  {NULL, NULL}
}

Where myfunction looks like:

void myfunction(BaseSequentialStream *chp, int argc, char *argv[])
{
   // do some stuff
}

I am using C++ and would like to implement some shell commands as class instance member functions (not static member functions). Is there a way to use anything in <functional> so that I can get a compatible function pointer from a class instance member function and register it with the array of ShellCommand structs? I've been reading about std::bind and std::function and am sort of getting the impression you can give them a pointer to a C-style function, but you can't get a pointer to a C-style function from them so it doesn't seem like it will work, but maybe I'm just missing something.

If anybody has a good approach to solving this problem, I would love to hear about it. It seems like I might have to implement it as a flat C-function that can then call something that will give it a pointer/reference to the instance I want and then once I have that I can forward the call to that instances member function. It just seems kind of messy and was hoping there was a better way.

Luke Peterson
  • 931
  • 1
  • 9
  • 25
  • By "C-style function" are you requiring the `extern "C"` language linkage, or merely an ordinary function (not function object or member function)? – Ben Voigt May 17 '13 at 20:49
  • I am not entirely sure but do know that class static functions work without the extern "C" wrapper -- would that imply then that the language linkage doesn't matter, only that the function be an ordinary function? – Luke Peterson May 17 '13 at 20:52
  • The linkage doesn't matter on many platforms, but it is not guaranteed that it doesn't matter. You are right that it must be an ordinary function, because member functions have an extra hidden parameter for passing `this`. On some platforms (Windows), `this` is not even passed in the same manner as other parameters. – Dietrich Epp May 17 '13 at 21:34
  • possible duplicate of [Function pointer to class member function problems](http://stackoverflow.com/questions/439540/function-pointer-to-class-member-function-problems) – mmmmmm May 18 '13 at 07:30

2 Answers2

1

No this can't be directly the way that you'd like it to. In a C interface you have no way to pass the implicit instance of the class somehow, a C interface simply doesn't have a slot for that. So you would have to pass the pointer to the instance in explicitly and call the class function. The gain that this would bring you is proctection of the other class methods.

All of this has nothing to do with the name mangling aspect of extern "C" linkage, but eventually with the ABI. Once you export a pointer to function from a compilation unit in a variable (here the array elements), it doesn't matter if the "orginal" symbol had been mangled or not. The type system guarantees that the function will always be called with the correct calling convention.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • `extern "C"` is important, but may not be relevant to the target system. See comment discussions [here](http://stackoverflow.com/q/16602617/315052) and [here](http://stackoverflow.com/q/15536488/315052). It has to do with the ABI, not the mangling. – jxh May 17 '13 at 21:14
0

Since you are using a C library, you should pass extern "C" functions to the C library. This is to make sure that you pass to the C library a function that is compatible to the C ABI (this is important on systems where the C ABI and the C++ ABI differ). Since the API looks very regular, you can use a macro to make this easier in the C++ code:

#define DEFINE_SHELL_COMMAND(cmd) \
    extern "C" void cmd##_c_wrap (BaseSequentialStream *chp, int argc, char *argv[]) { \
        cmd().execute(chp, argc, argv); \
    } \
    ShellCommand cmd##Entry = { #cmd, cmd##_c_wrap }

class ShellCommandClass {
protected:
    virtual ~ShellCommandClass () {}
public:
    virtual void execute (BaseSequentialStream *chp, int argc, char *argv[]) = 0;
};

class myfunctionClass : public ShellCommandClass {
    //...
public:
    void execute (BaseSequentialStream *chp, int argc, char *argv[]) { /* ... */ }
};

ShellCommandClass & myfunction () {
    static myfunctionClass cmd;
    return cmd;
}

DEFINE_SHELL_COMMAND(myfunction);

static const ShellCommand commands[] = {
    myfunctionEntry,
    {NULL, NULL}
}
jxh
  • 69,070
  • 8
  • 110
  • 193