user3429251
user3429251

Reputation: 121

c function pointer pass parameter during runtime

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?

Upvotes: 1

Views: 528

Answers (4)

Joseph Quinsey
Joseph Quinsey

Reputation: 9972

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

Upvotes: 1

Akshit
Akshit

Reputation: 19

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

Upvotes: 0

flatmush
flatmush

Reputation: 221

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;
}

Upvotes: 1

Shahbaz
Shahbaz

Reputation: 47563

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);

Upvotes: 0

Related Questions