0

I want to do something like this:

struct CLI_Command{
    CLI_Command(char* s, void (*h)(void)){
        command_string = s;
        handler = h;
    }

    char* command_string;
    void (*handler)(void);
};

class CLI  {
    public:
        CLI();

    private:
        CLI_Command cli_table[NO_CLI_COMMANDS] = {
                CLI_Command("Command1",   handler1),
                CLI_Command("Command2",   handler2)
        };

        void handler1(){};
        void handler2(){};
};

I know that I need something similar to CLI::*handler, but I can't get the syntax right. I keep running into errors like this:

"error: no matching function for call to 'CLI_Command::CLI_Command(const char [4], <unresolved overloaded function type>)"
Jolle
  • 1,336
  • 5
  • 24
  • 36
  • The problem is that you are trying to take the address of a non-static member function, which is disallowed. I have [answered](http://stackoverflow.com/a/25953204/1392132) a similar question recently. Maybe it will help you. Note that modern C++ provides powerful tools to write your program much cleaner and safer. – 5gon12eder Oct 10 '14 at 18:50
  • 1
    @5gon12eder: It's perfectly legal to take the address of a non-static member function, but the result is a pointer-to-member, not a function pointer. – Ben Voigt Oct 10 '14 at 18:52
  • @5gon12eder OK, can you elaborate on these tools? Or point me in some direction? :) . I am making a command line interface through an UART on an embedded device. – Jolle Oct 10 '14 at 18:56
  • @BenVoigt Yes, that's of course correct. So the disallowed thing is “taking the address of a non-static member function an use it as a function pointer”. – 5gon12eder Oct 10 '14 at 18:57
  • @Jolle For example, you could make your table a `std::map` where `Command` could be an appropriate `typedef` or – if you want more OO and can live with the overhead, create a `CommandInterface` and put polymorphic command objects in your map that you invoke virtual functions on. – 5gon12eder Oct 10 '14 at 19:00

5 Answers5

3

This illustrates the correct syntax:

class CLI;

struct CLI_Command
{
    CLI_Command(char* s, void (CLI::*h)(void))
    {
        command_string = s;
        handler = h;
    }

    char* command_string;
    void (CLI::*handler)(void);

    void raise( CLI* the_cli ) { return (the_cli->*handler)(); }
};

class CLI
{
    public:
        CLI();

    private:
        static CLI_Command cli_table[NO_CLI_COMMANDS];

        void handler1(){};
        void handler2(){};
};

CLI::CLI_Command cli_table[NO_CLI_COMMANDS] = {
  { "Command1",   &CLI::handler1 },
  { "Command2",   &CLI::handler2 }
};

Names of member functions do not decay to pointer-to-member. You must use & explicitly, and a qualified name, when creating a pointer-to-member.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

In addition to other answers, another option is to use std::function together with std::bind():

struct CLI_Command{
    ...
    std::function<void> handler;
};

class CLI {
    ...
    CLI_Command cli_table[NO_CLI_COMMANDS] = {
      { "Command1",   std::bind(&CLI::handler1, this) },
      { "Command2",   std::bind(&CLI::handler2, this) }
    };

    void handler1(){};
    void handler2(){};
};
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • Oh cool. Do you have an idea of how much overhead this will produce? My target is a little embedded device, so I can't afford to much overhead :) . – Jolle Oct 10 '14 at 19:07
  • @Jolle no problem, this is for completeness so the question might be helpful to other people. – Anton Savin Oct 10 '14 at 19:09
  • actually it was a question, because I have no idea if this will produce any overhead :) – Jolle Oct 10 '14 at 19:15
  • Heh :) Actually it may bring some overhead depending on implementation, but on other hand it brings more flexibility. – Anton Savin Oct 10 '14 at 19:42
1
void handler1(){}
void handler2(){}

are member functions of CLI. The correct way to "address to" them is &CLI::handler1 and not handler1. However then, they won't be accepted by void (*h)(void), which would need to be changed to void (CLI::*h)(void). But that is probably not what you want.

Maybe consider reading about std::function for type erasure, or make your handler1/handler2 static.

Slyps
  • 607
  • 4
  • 9
1

You should use the syntax for a pointer to class member instead of the syntax for a loose function pointer.

class CLI;

struct CLI_Command{
    CLI_Command(char* s, void (CLI::*h)(void)){
        command_string = s;
        handler = h;
    }

    char* command_string;
    void (CLI::*handler)(void);
};

In addition, make sure you call the function through the pointer of the current CLI class;

void CLI::process(char *cmd) {
    CLI_Command command* = /* lookup the command */
    this->(command->handle)();
}
0

To get it working, make your methods static

static void handler1(){};
static void handler2(){};

Whatever consequences (read here please, for more detailed info) this will have :-( .

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190