2

Can I declare an opaque function pointer type, without a full prototype, and initialize a structure containing a pointer to a function of that type, without fleshing out the signature (which will be full declared at the point of use).

For example:

// file api.h

typedef void (*function_ptr)(???);

typedef struct {
  function_ptr func;
} S;

function_ptr foo;
function_ptr bar;

const S structs[] = { {foo}, {bar} };

// file impl.c

typedef ret_type (*function_ptr)( // complex argument list );

void foo( // complex argument list) {
...
}

void bar( // complex argument list) {
...
}

structs[0].func(arg1, arg2, arg3);

Basically I want to hide the details of the function prototype (the return type and the parameter list) from the api.h header file, but still declare structs that use a pointer to this function. At the location of the declaration of any such functions, or of their use, the full prototype should be in scope, and argument checking will occur, etc.

A similar pattern works fine with forward-declared structs: you could forward declare an opaque struct C and use pointers to that struct in other structures, including static initialization of pointers to C eg:

struct C;

typedef struct {
    struct C* c;
} D;

extern struct C c;

D d[] = {
        { &c }
};

The details of C are totally hidden. I'm looking for the equivalent pattern for function pointers.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • What property of the function slot do you need to be visible outside the file? Just the fact that it takes up space in the struct and that you want it to typecheck inside the file where the actual type is known? – Zalman Stern Dec 02 '17 at 01:44
  • @ZalmanStern - correct. All function pointers are convertible to all others, so there shouldn't any kind of "varying pointer size" behavior to cope with. – BeeOnRope Dec 02 '17 at 01:47
  • `const S structs[] = { {foo}, {bar} };` is an error: initializers for static variables must be constant expressions. Also it will lead to a multiple definition error having that in a header. – M.M Dec 02 '17 at 01:49
  • @M.M - right, in fact the definition is actually in an `.c` file. Yes, the `{foo}` thing doesn't work but consider that file kind of pseudo-code for what I want to do since I don't know the right syntax (it would certainly work if `foo` were a function, for example. – BeeOnRope Dec 02 '17 at 02:10
  • If you just want the pointer and don't care about the type checking, `void *` is your friend. Generally the approach people take is to have a struct that represents a vtable (a number of function pointers) and the entire struct is indirect and opaque. – Zalman Stern Dec 02 '17 at 02:17
  • 1
    @ZalmanStern - I don't think you can safely cast a function pointer to `void *` and back again. Pointers to functions and pointers to everything else don't necessarily have the same representation or size. That said, I put a solution along those lines [below](https://stackoverflow.com/a/47603934/149138) - but it an arbitrary function pointer type (`void (*)(void)` to avoid that problem. – BeeOnRope Dec 02 '17 at 02:25
  • Why are you exposing this function pointer to the user of your API? They aren't going to be able to call the function, so you should hide it in the private implementation, where you can give it the full correct prototype. – Jonathan Leffler Dec 02 '17 at 08:41
  • @JonathanLeffler - yes, I don't want to expose the function pointer except for the fact that it takes storage in `struct S`. Basically in the actual application `struct S` is larger and contains semantically public elements (most of them) which are intentionally exposed. In addition, it has an opaque function pointer and a pointer to an opaque `struct` which are to be used only in a specific implementation file. It is easy to the the details of the opaque struct via forward-declaration. I want to similarly hide the details of the function pointer. – BeeOnRope Dec 02 '17 at 20:35
  • Read up on the C++ [Pimpl Idiom](http://en.cppreference.com/w/cpp/language/pimpl) and [opaque pointers](https://en.wikipedia.org/wiki/Opaque_pointer) (and there are other places you can find that information too). Use it; embed a pointer to an opaque structure in your type with exposed data elements; access it when needed. However, I have reservations about how that value will be set; how will users tell you when to change that pointer? They can't provide the function that will be used, so you'll provide an interface to change that function pointer. I'd do my utmost to avoid exposing it. – Jonathan Leffler Dec 02 '17 at 20:42
  • @JonathanLeffler - yup, I'm well aware of PIMPL and similar techniques (note this question is C not C++, but PIMP applies as well in C). I'm basically just wondering if there is any equivalent specifically for _function pointers_ like there is for pointers of all other types (a forward declaration is all that's needed for a type-safe opaque non-function pointer, or `void *` works as well). Note that I accepted the answer that describes wrapping the function pointer in a struct which seems to be the best compromise. – BeeOnRope Dec 02 '17 at 20:44
  • About setting the pointer: yes, definitely if you have public and opaque fields mixed in a struct this is an issue in general. In my particular case the structs are all declared `const` and cannot be modified (there are a fixed number allocated statically in an array for the duration of the application). – BeeOnRope Dec 02 '17 at 20:45
  • You can use `void (*function)(void)` as a 'universal function pointer' as long as you convert back to the correct types. ISO/IEC 9989:2011 [§6.2.3.3 Pointers](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3) ¶8 _A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined._ (And yes, I am/was aware this is C, but C++ seemed apposite for what you're doing.) – Jonathan Leffler Dec 02 '17 at 20:51
  • Correct - see for example [my answer](https://stackoverflow.com/a/47603934/149138) which covers this approach in detail. The main problems are (1) you need to cast at the point of invocation (this problem doens't occur with a forward-declared `struct`) but more importantly it is hard to initialize this pointer a function during static initalization: if the function was in scope then you have the original problem (exposure of the full signature). If it's not then how do you assign it? I added an extra layer of indirection to solve it in my answer, but it's ugly. Can you see anything better? – BeeOnRope Dec 02 '17 at 20:54

3 Answers3

2

In C, the typedef

typedef void (*function_ptr)();

associates the type void (*) () with function_ptr. The type void (*) () means "a pointer to a function that takes an unspecified number of arguments and returns void," and you can call such a function pointer with any number of arguments that you'd like. This might match what you're looking for.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • I used `void` in the example above for my "I don't know how to declare this" although the type isn't actually `void` (I'd also like to hide it). I'd like to get type checking at the actual invocation sites of this function, I guess I won't get this with that? – BeeOnRope Dec 02 '17 at 01:42
  • 1
    @BeeOnRope Yep, you're going to lose that if you approach things this way. Perhaps you're better off hiding the entire struct? – templatetypedef Dec 02 '17 at 01:43
1

Can't think of any meaningful way this is possible in C. (In C++ there are some solutions that probably meet the use case.) The main reason is there's not really any use in knowing something is a function pointer without any of the concrete info. A struct has a name, which needs to obey ODR rules, so there is a notion of type comparison with no details other than the name.

The opaque struct pointer technique is best used to hide everything inside a struct from those outside a file. So putting the function pointer inside a struct is likely the best bet. This may force an extra level of indirection.

Zalman Stern
  • 3,161
  • 12
  • 18
  • The above answer points out that you can take advantage of pre-ANSI C prototypeless behavior. I don't think that helps much, but it does exist. – Zalman Stern Dec 02 '17 at 01:43
  • Accepted as the "opaque struct" seems to offer the best approach here (it seems to be pretty much strictly better than my answer of using a pointer to a function pointer, since both involve indirection and this is much cleaner. Sometimes it may come for free if you already have a forward-declared struct you can hide the pointer in. – BeeOnRope Dec 02 '17 at 20:37
1

One ugly workaround is to declare your pointers of any valid function pointer type (e.g., the simplest void returning 0-arg function like void (*voidfunc_ptr)(void)) in your api.h file and then cast them to the actual type at invocation time. You'll then need to expose the actual function pointers to the api.h file. You can't use the function itself since it isn't really of the right type (probably it would work if you declared your functions voidfunc in one place and used their full definition elsewhere, but it's an ODR violation).

One way you can do that is to use double indirection: in a file where the function implementations are visible, assign a function pointer to a voidfunc global.

Here's a sketch:

// file api.h
typedef void (*voidfunc_ptr)(void);

typedef {
    voidfunc_ptr* f;
} S;

extern voidfunc_ptr foo_p;
extern voidfunc_ptr bar_p;

S array_of_s[] = { {&foo_p}, {&bar_p} };

// file impl.h

typedef int (*real_function_ptr)( //complex signature... );

// file impl.c which defines foo and bar
int foo( //complex signature... ) { ... }
int bar( //complex signature... ) { ... }

voidfunc_ptr foo_p = (voidfunc_ptr)foo;
voidfunc_ptr bar_p = (voidfunc_ptr)bar;

// some file that uses impl.h and knows about real_function_ptr

void callS(S s) {
  ((real_function_ptr)(*s.f))(// pass complex args); 
}

void use_array() {
  callS(array_of_s[0]);
}

So you completely hide the type of the function pointer in the api.h file, but one cost is double indirection (you have to store a voidfunc_ptr* which is a pointer-to-a-function-pointer) in order to get allow static initialization from an extern voidfunc_ptr. If you didn't need static initialization, you could avoid the double indirection.

Another cost is an ugly call side: you need to cast the voidfunc_ptr back to the real underlying pointer type. Perhaps there is a way to move the pain elsewhere by declaring another struct like S2 that puns S to contain the correct pointer type (I'm not sure if this is legal). If you forget to do this cast you could call the function as a voidfunc_ptr possibly with disastrous results.

The upside is that you still get parameter type checking at the call sites (after the cast).

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • Does the C spec guarantee that it's safe to cast function pointer types to one another? I seem to remember that in C++ that's undefined behavior and I suspect C might not like that either. – templatetypedef Dec 02 '17 at 02:24
  • 1
    @templatetypedef - yes, see [this answer](https://stackoverflow.com/a/559671/149138) for example. Essentially you can pass or store _any_ function pointer as any other function pointer, but it must have the correct type when you call it. So any function pointer serves a similar function as `void *` for other types of pointers. – BeeOnRope Dec 02 '17 at 02:27