-1

I have some straightforward C-style code that implements a simple command interpreter as follows:

typedef struct {
    char *name;
    int (*func)(char* args);
} sCOMMAND;

int func_reset(char* args); 
int func_acc(char* args);
int func_dec(char* args);

const sCOMMAND CmdTbl[] = {
    { "RST",  func_reset },
    { "ACC",  func_acc },
    { "DEC",  func_dec }
};

void ProcessCommand (char *cCmd)
{
    int ndx = NUM_CMDS;

    // shortest cmd is 3 chars
    if (strlen(cCmd) >= MIN_CMD_LEN)
    {
        /////////////////////////////////////////////////////
        // Chop the command line into substrings with ptr to
        // each one where *parm[0] points to the command.
        char *parms[MAX_PARMS];
        ParseCommand(cCmd, parms); 

        /////////////////////////////////////////////////////
        // Scan the command table looking for a match
        for (int ndx = 0; ndx < NUM_CMDS; ndx++)
        {
            if (stricmp (CmdTbl[ndx].name, &parms[0]) == 0)
            {
                // command found, run its function
                CmdTbl[ndx].func(parms);  
            }
        }
    }

    // no valid command was found
    if (ndx == NUM_CMDS)
        Serial.println("UNKNOWN COMMAND");

    Serial.println("RDY> ");
}

This works, but what I'd like to do is encapsulate the command interpreter in a C++ class. But I haven't been able to get the sCOMMAND table set up with class methods. My recent attempt is:

// in file CmdHandler.h

class CmdHandler;

typedef struct {
    char *name;
    int (CmdHandler::*func)(char* args);
} sCOMMAND;


class CmdHandler 
{
    public:
        CmdHandler(int buf_size);
        ~CmdHandler(void) { if (m_Buf) delete [] m_Buf; };
        void ProcessCommand (char *cCmd);

    private:
        char        *m_Buf;
        int         m_BufSize;
        char        *parms[MAX_PARMS];
        void ParseCommand(char *cCmd, char* (&parms)[MAX_PARMS]); 

        int func_1(char* args); 
        int func_2(char* args);
        int func_3(char* args);

        sCOMMAND    CmdTbl[3];
};

// in file CmdHandler.cpp

#include "CmdHandler.h"


CmdHandler::CmdHandler(int buf_size)
    : CmdTbl{   {"RST", func_1},
                {"ACC", func_2},
                {"DEC", func_3} }
{
    m_BufSize = buf_size;

    m_Buf = new char[m_BufSize];
}

void CmdHandler::ProcessCommand (char *cCmd)
{

}

On ARM GCC CPP Compiler, this gives an error "cannot convert 'CmdHandler::func_1' from type 'int (CmdHandler::)(char*)' to type 'int (CmdHandler::*)(char*)'".

If I take the pointer reference out of the struct as:

typedef struct {
    char *name;
    int (CmdHandler::func)(char* args);
} sCOMMAND;

I get

"CmdHandler.h: invalid use of incomplete type 'class CmdHandler'
      int (CmdHandler::func)(char* args);".

Can anyone tell me how to solve this, or suggest any better way?

RayZ
  • 29
  • 1
  • 1
  • 4

1 Answers1

0

Unlike non-member functions, member functions don't decay to function pointers. Hence, you need to use:

CmdHandler::CmdHandler(int buf_size)
    : cmd_tbl{  {"RST", &CmdHandler::func_1},
                {"ACC", &CmdHandler::func_2},
                {"DEC", &CmdHandler::func_3} }
{
    m_BufSize = buf_size;

    m_Buf = new char[m_BufSize];
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • This compiled, but now gives linker error (for each method): "undefined reference to `CmdHandler::func_1(char*)'". This was marked as a duplicate; let me look at the original... – RayZ Jun 02 '17 at 21:01
  • 1
    @RayZ, I suggest creating a [mcve] that produces the linker error and asking that in a separate SO post. – R Sahu Jun 02 '17 at 21:04
  • @RayZ Did you implement these functions (since you did not in the provided code sample)? – drescherjm Jun 02 '17 at 21:11
  • @RayZ At a glance, it's probably because member functions have an implicit first argument of `this` that gets passed for all calls to a given member function. You'll need to update the signature of your function pointer to take a `CmdHandler *` as its first argument, and a `char *` as the second argument. – Xirema Jun 02 '17 at 21:12
  • Thanks for all of the quick feedback. @drescherjm, yes, these are implemented as listed except for pulling in another h file with MAX_PARMS. I'm still trying to apply the discussion in the possible duplicate question "How to make an array of function pointers private in C++". – RayZ Jun 02 '17 at 21:28
  • @RSahu you were exactly correct. The linker error that resulted was just a stupid attack on my part because I hadn't implemented `func_x's`. Thanks! – RayZ Jun 02 '17 at 21:55
  • @RayZ, drescherjm mentioned that. Regardless, I am glad you understand the problem. I am sure the fix is easy. – R Sahu Jun 02 '17 at 21:58