Storing function pointers generically
  • DrewDrew July 2008
    Hi,
    I want to add a function table to my project with the intent of being able to look up & execute methods using strings. I can't guarantee the signatures of the functions I want to add to the table will all be the same, or even what they are going to be, so I need a generic solution.

    However I can't figure out how to store a bunch of random function pointers in one place - how to go from something like
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // define the function pointers
    int (*pFunc)(int) = NULL;
    void (Class::*pMember)(int,bool)= NULL;
    bool (Class::*pMember2)(void) const = NULL;
     
    // create some example instances
    pFunc staticMethod = &someFunction;
    pMember nonstaticClassMethod = &someClass::someFunction;
    pMember2 constClassMethod = &someOtherClass::someOtherFunction;


    to
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // pseudocode (I haven't decided exactly which datatypes I'll be using yet)
    // the multimap class stores a GenericFunctionPointer & indexes it
    // with 2 strings - the class instance ("" means global) & function name
     
    multimap<GenericFunctionPointer,string,string> FunctionTable;
    FunctionTable->Add(staticMethod,"","someFunction");
     
    // i'm also storing the class instances for these two ;)
    FunctionTable->Add(nonstaticClassMethod,"someClass","someFunction");
    FunctionTable->Add(constClassMethod,"someOtherClass","someOtherFunction");


    Is it possible? Am I approaching the function table idea from the right angle? Any advice would be appreciated.

    edit:
    One solution I can see would be to have the function pointer point to a wrapper function that calls the function I want, but has no parameters & returns void.
    Then I could store & retrieve the parameters & result somewhere accessible by both the calling code & the function it's calling, sort of like a software cpu register I guess.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    bool ICanHasPie(float desireForPie)
    {
    if (desireForPie >= 3.14159265f)
    return true;
    else
    return false;
    }
     
     
    void someFunctionWrapper()
    {
    float Param1 = registerTable->GetRegister(0);
    bool Result = ICanHasPie(Param1);
    registerTable->SetRegister(1,Result);
    }
     
    void (*pFunction)(void) = NULL;
     
    // functiontable now multimap<pFunction,string,string>
    pFunction function = &someFunctionWrapper;
    FunctionTable->Add(function,"","ICanHasPie");
     
    // call the function:
    float aValue = 5;
    bool Result;
    registerTable->SetRegister(0,aValue);
    FunctionTable->CallFunction("","ICanHasPie");
    Result = registerTable->GetRegister(1);
    if (Result)
    {
    Pie->CutSlice();
    }


    However whilst that makes the function table pretty straightforward the additional plumbing required on the calling side of things would turn hooking up a function into a fairly heavyweight task with all the wrappers & dealing with registers, which I'd prefer to avoid if possible. Also the registers still dont allow for a generic solution here. At least in their current form. Hmm...

    edit2: in fact the bit above is probably complete rubbish - I forgot to think about class function pointers. I suppose maybe it could be dealt with at the wrapper level.. arrgh, I think I'm just shifting the problem around rather than fixing it!
  • The boost libraries provide a few ways to do what you want. Have a look at Boost.Function. You may have to combine Boost.Function with Boost.Bind or Boost.Lambda to get exactly what you want.

    I don't use boost, so I can't help you with specifics. When I found myself needing boost functionality, I switched to C#, which has that kind of functionality built in.
  • DrewDrew July 2008
    Thanks for the tip Prof. I hadn't even thought about Boost to be honest, I didn't realise I'd wandered into that kind of territory either :shock: though I'm hoping this is something I can achieve without a library. I had a couple of ideas from reading those docs (involving a FuncPtr class with a of bunch function pointer declarations & void* casts, or messing with std::mem_fun/mem_ref) but if those don't work out it's good to know I have something to fall back on without using a scripting library.
  • KalithKalith July 2008
    I haven't played with function pointers yet, but I think you can do the same way as LUA does.
    I haven't looked into LUA sources neither, but LUA functions in C++ always have the same signature :
    int function(lua_State*)
    ... "lua_State*" beeing a pointer to the stack (it's not actually true, but I'll simplify for the sake of the example) and "int" the number of returned values.

    You can use the same system :
    Create a stack in which you'll push your function arguments (a std::vector, why not), call your wrapper function, retrieve your arguments, call your true function (and optionally, return value(s)).
    You'll do all the argument checking in the wrapper function : do I have enough arguments, with the required type, ...

    Example of a LUA wrapper function I use :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    int l_Unit_SetAnim( lua_State* luaVM )
    {
    bool error = false;
    int n = lua_gettop(luaVM); // Get the number of values on the stack
    // Our function takes a minimum of 2 arguments, plus 3 other optional ones
    if (n < 2)
    {
     
    LUA::PrintError(luaVM, "Too few arguments in \"UnitSetAnim\" (2 expected : unit id, anim id, (+priority, queued, reset))");
    // So if there isn't enough, just return and do nothing
    return 0;
    }
    // We then check the value type
    if (!lua_isnumber(luaVM, 1))
    {
    LUA::PrintError(luaVM, "Argument 1 of UnitSetAnim must be a number (unit id)");
    error = true;
    }
    if (!lua_isnumber(luaVM, 2))
    {
    LUA::PrintError(luaVM, "Argument 2 of UnitSetAnim must be a number (anim id)");
    error = true;
    }
    // We fill the stack with nil (NULL) values until there are 5 values
    if (n < 5)
    for (int i = n; i < 5; i++)
    lua_pushnil(luaVM);
     
    // And we check optional arguments
    AnimPriority priority = ANIM_PRIORITY_HIGH;
    if (!lua_isnumber(luaVM, 3) && !lua_isnil(luaVM, 3))
    {
    LUA::PrintError(luaVM, "Argument 3 of UnitSetAnim must be a number (priority)");
    }
    else
    priority = (AnimPriority)ToInt(lua_tonumber(luaVM, 3));
     
    bool queued = false;
    if (!lua_isboolean(luaVM, 4) && !lua_isnil(luaVM, 4))
    {
    LUA::PrintError(luaVM, "Argument 4 of UnitSetAnim must be a bool (queued)");
    }
    else
    queued = lua_toboolean(luaVM, 4);
     
    bool reset = false;
    if (!lua_isboolean(luaVM, 5) && !lua_isnil(luaVM, 5))
    {
    LUA::PrintError(luaVM, "Argument 5 of UnitSetAnim must be a bool (reset)");
    }
    else
    reset = lua_toboolean(luaVM, 5);
     
    // Then, if everything went fine, we call our function
    if (!error)
    {
    Unit* u = Frost::mUnitMgr->GetUnitByID(ToInt(lua_tonumber(luaVM, 1)));
    if (u != NULL)
    {
    bool success = u->SetAnimState((AnimID)ToInt(lua_tonumber(luaVM, 2)), priority, queued, reset);
    // Push our return value on the stack
    // I think LUA pushes it on the same stack, and just retrieve
    // the X last values on the stack, where X is the value returned
    // by this wrapper function
    lua_pushboolean(luaVM, success);
    }
    }
    else
    {
    // If something went wrong, we just return a nil value
    // You can make your system smart enough to fill the gap by itself
    lua_pushnil(luaVM);
    }
     
    // At last, we return the number of returned value, here 1
    return 1;
    }


    Hope that helps ;)
  • DrewDrew July 2008
    Cheers Kal, definitely helps. I can see this getting dangerously close to implementing an entire VM architecture which I was trying to avoid as well :)
    Though thinking about it briefly ('tis time for tea & beer here) I'll probably end up with one anyway as I guess I'll be needing more than just a function table in order to provide access to user-defined types etc.. May be easier to just get it done I guess.

    Why don't I just use a scripting library I hear you ask? Well, I want to have a conversion phase that takes script classes & outputs c++ code (though purely script-based functions are still allowed), so all that wrapper code would be generated for me. Ignoring the cost of having to write it myself, this gives me two benefits: less repetitive typing for me & a scalable script system that isn't tied into a predetermined set of c++ functions. None of the off-the-shelf libraries I've seen offer something like that. Which is a shame, because this approach forces me to design a grammar as well, so I lose the benefit of having a well-tested language to base things on.
  • DaiShivaDaiShiva July 2008
    Are you talking about using antlr?
  • DrewDrew July 2008
    I'd planned on using Flex & Yacc as that's what I used at uni. I hadn't heard of Antlr, looks a lot more advanced though - I'll certainly check that out before I start.

    edit: I've been playing with antlrworks for a couple of hours & like what I've seen so far, what I'm really impressed by is the workflow. Being able to write, run & debug it all in an ide.. that is going to save heaps of time.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sign In Apply for Membership

In this Discussion

Who's Online (1)