1

I have a pretty complex problem about c function pointers and passing the parameters to them.

I have a function pointer and a couple of function addresses within a lookup table. I get all of my data via a serial interface. First the number of the function which has to be called. I look it up in the table and pass the reference to my function pointer.

After that, i receive several pairs of 4 byte values as data as the arguments. Problem is, i have to call different functions with the same return type but a different amount of parameters.

Is there a way to pass dynamically data to a function call. Maybe by pushing them on the stack manually? Couldn't find a solution for that.

Does anybody has any idea or a hint to solve that problem?

4
  • 4
    Maybe it would help to show some code, and what you have tried so far? Commented Mar 19, 2014 at 16:06
  • 4
    have you looked into "Variadic Functions"? Commented Mar 19, 2014 at 16:07
  • 1
    You can't dynamically generate a function call in C. Either have a big switch that will perform the correct call for you based on the function, or you'll have to drop to assembler to perform the calls (they are not always pushed on the stack, you will have to find out the calling convention). There might be compiler extensions available, but I've never heard of the ones that would help with this. Commented Mar 19, 2014 at 16:16
  • The base question is if all possible permutation of parameter types are known on compile time. Commented Mar 19, 2014 at 17:08

4 Answers 4

1

I don't believe there's an easy way to answer this since argument passing is ABI (Application Binary Interface) specific. If your platform is fixed and you don't mind writing non-portable code then you can write your code in an ABI specific way but I wouldn't advise that.

I've solved this issue in a small emulator I wrote, for me it was easier since the number of commands was never greater than 4, I basically used a union of function pointers which had the number of arguments I wanted and a switch statement to select the right one.

typedef struct
{
    union
    __attribute__((__packed__))
    {
        void      *func;
        void     (*func_a0)(void);
        uint32_t (*func_a0r)(void);
        void     (*func_a1)(uint32_t);
        uint32_t (*func_a1r)(uint32_t);
        void     (*func_a2)(uint32_t, uint32_t);
        uint32_t (*func_a2r)(uint32_t, uint32_t);
        void     (*func_a3)(uint32_t, uint32_t, uint32_t);
        uint32_t (*func_a3r)(uint32_t, uint32_t, uint32_t);
        void     (*func_a4)(uint32_t, uint32_t, uint32_t, uint32_t);
        uint32_t (*func_a4r)(uint32_t, uint32_t, uint32_t, uint32_t);
    };
    unsigned    args;
    bool        ret;
    const char* name;
} jump_entry_t;

bool jump_table_exec(
    jump_table_t* table, void* addr,
    uint32_t* args, uint32_t* ret)
{
    #ifdef JUMP_TABLE_DEBUG
    if (!table)
        return false;
    #endif

    if ((uintptr_t)addr < (uintptr_t)table->base)
        return false;
    unsigned i = ((uintptr_t)addr - (uintptr_t)table->base);
    if ((i & 4) || (i >= table->size))
        return false;

    jump_entry_t j = table->entry[i >> 3];
    if (!j.func)
        return false;
    if (j.args && !args)
        return false;

    if (j.ret)
    {
        if (!ret) return false;
        switch (j.args)
        {
            case 0:
                *ret = j.func_a0r();
                break;
            case 1:
                *ret = j.func_a1r(args[0]);
                break;
            case 2:
                *ret = j.func_a2r(args[0], args[1]);
                break;
            case 3:
                *ret = j.func_a3r(args[0], args[1], args[2]);
                break;
            case 4:
                *ret = j.func_a4r(args[0], args[1], args[2], args[3]);
                break;
            default:
                return false;
        }
    }
    else
    {
        switch (j.args)
        {
            case 0:
                j.func_a0();
                break;
            case 1:
                j.func_a1(args[0]);
                break;
            case 2:
                j.func_a2(args[0], args[1]);
                break;
            case 3:
                j.func_a3(args[0], args[1], args[2]);
                break;
            case 4:
                j.func_a4(args[0], args[1], args[2], args[3]);
                break;
            default:
                return false;
        }
    }

    #ifdef JUMP_TABLE_DEBUG
    if (j.name)
    {
        fprintf(stderr, "Info: Jump table %s(", j.name);
        if (j.args >= 1) fprintf(stderr,   "%" PRIu32, args[0]);
        if (j.args >= 2) fprintf(stderr, ", %" PRIu32, args[1]);
        if (j.args >= 3) fprintf(stderr, ", %" PRIu32, args[2]);
        if (j.args >= 4) fprintf(stderr, ", %" PRIu32, args[3]);
        fprintf(stderr, ")");
        if (j.ret) fprintf(stderr, " returned %" PRIu32, *ret);
        fprintf(stderr, ".\n");
    }
    #endif
    return true;
}
Sign up to request clarification or add additional context in comments.

Comments

1

Sometimes the following approach using unions is useful:

union foo {
   struct {
      int arg1;
   } f1_args;
   struct {
      int arg1, arg2;
   } f2_args;
};
int f1(union foo*);    
int f2(union foo*);
int (*table[])(union foo*) = {f1, f2};
//...
union foo data;
//...
int answer = table[1](&data); // calls f2, which uses arg1 and arg2

And, if you prefer, f1 and f2 can be simple wrappers to the "real" functions, as in:

int f1(union foo *u) { return f1_real(u->f1_args.arg1); }
int f2(union foo *u) { return f2_real(u->f2_args.arg1, u->f2_args.arg2); }

This is quite flexible. But if your arguments are always only 4-byte ints, then you can get rid of the union and just use arrays. Rewritten, the above becomes:

int f1(uint32_t *a) { return f1_real(a[0]); }       // wrapper
int f2(uint32_t *a) { return f2_real(a[0], a[1]); } // wrapper
int (*table[])(uint32_t *) = {f1, f2};              // lookup table
//...
uint32_t data[99];                                  // data from e.g. serial port
//...
int answer = table[1](data);                        // calls f2, which uses two args

Comments

0

Since the functions can distinguish their parameters, you can always give them a ... type. For example:

int f(...)
{
    /* extract one int */
}

int g(...)
{
    /* extract two floats */
}

...

int (*fp)(...);

if (type_one)
    fp(10);
else if (type_two)
    fp(1.3, 4.3);

Or better yet use a union. However, in your particular case, since the parameters themselves are "pairs of 4 bytes", you can always use an array:

struct arg
{
    uint32_t pair_of_4_bytes[2];
};

int f(struct arg *args, size_t count)
{
}

int g(struct arg *args, size_t count)
{
}

...

int (*fp)(struct arg *args, size_t count);
struct arg args[MAX];
size_t count = 0;

/* get args from serial and put in args/count */
fp(args, count);

1 Comment

You need at least one named parameter so int f(...) won't work.
0

I think "Variadic functions" can solve your problem needs. Checkout a simple example here:

http://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html#Variadic-Example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.