简体   繁体   中英

ANSI C - multiple function pointers assignment

I have a struct which looks somewhat like this:

struct Data
{
  int a;
  float b;
  char *c;

  int (*read)(struct Data *data, int arg1);
  int (*write)(struct Data *data, int arg1, int arg2);
  int (*update)(struct Data *data, int arg1, int arg2, int arg3);
  int (*erase)(struct Data *data, int arg1);
  /* ... */
}

The ... means that there is bunch of other function pointers smiliar to above (that is, they all return an int and take pointer to Data as first argument, but other arguments may differ).

Let's say there are 20 function pointers total. In a special function DataInit() , I assign functions to them, like this:

Data->read = readA;
Data->write = writeA;
/* readA() and writeA() are functions defined elsewhere in the code, with argument lists same as corresponding function pointers */

Now I have to do the same for another object of type Data, which differs in a way that it's "read-only"; it basically means that from those 20 function pointers 15 has to be assigned such that after invoking them they should return error code NOT_SUPPORTED. The rest stay the same (for example, readA() is assigned to function pointer ( *read ) like above).

I was wondering if there's a way to do it without implementing a function for each pointer (for example, updateB() that takes three arguments and its body is just return NOT_SUPPORTED ). Unfortunately, I cannot just set them to NULL .

I was thinking about preprocessor macros but it's black magic to me, honestly.

No, you may not cast a function pointer to a function pointer of different type (or even worse, to a different pointer type). This causes undefined behavior in the C standard for a good reason.

There are currently architectures out there where this isn't just a theoretical problem that everyone gets away with, but it can actually crash your program in unexpected ways. Read this blog post if you want details.

I don't know whether my suggestion is legal or not, but I want to suggest this:

int data_not_supported_(struct Data *thiz, ...)
{
    return NOT_SUPPORTED;
}

And there might be no problem if your compiler uses cdecl calling convention, where the number of argument doesn't affect on the caller.

Yes, you can use a single function

int unsupported() {
    return NOT_SUPPORTED;
}

and cast to correct the function pointer type when initializing your struct:

Data->write = (int (*)(struct Data *, int, int))unsupported;

These casts are ugly, so it's more readable to have a typedef for each function:

typedef int
(*write_t)(struct Data *, int, int);

And then:

Data->write = (write_t)unsupported;

As mentioned, function pointer casts will most likely result in undefined behavior on most systems.

A feasible solution to the problem is this:

typedef int func_t (struct Data* this, void* arg);

struct Data
{
  int   a;
  float b;
  char* c;

  func_t* read;
  func_t* write;
  ...
};


// later on in the code:
int update_function (struct Data* this, void* arg)
{
  struct my_type* m = (struct my_type*)arg;

  // use m
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM