我可以在没有完整签名的情况下声明“opaque”函数指针类型吗?

时间:2017-12-02 01:35:42

标签: c function-pointers

我是否可以在没有完整原型的情况下声明 opaque 函数指针类型,并初始化包含指向该类型函数的指针的结构,而不会充实签名(将在使用点)。

例如:

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

基本上我想从api.h头文件中隐藏函数原型的详细信息(返回类型和参数列表),但仍然声明使用指向此函数的指针的struct。在声明任何此类函数或其使用的位置,完整的原型应该在范围内,并且将进行参数检查等。

类似的模式适用于前向声明的结构:你可以转发声明一个不透明的struct C并在其他结构中使用指向该结构的指针,包括指向C的指针的静态初始化,例如:

struct C;

typedef struct {
    struct C* c;
} D;

extern struct C c;

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

C的详细信息完全隐藏。我正在寻找函数指针的等效模式。

3 个答案:

答案 0 :(得分:2)

在C中,typedef

typedef void (*function_ptr)();

将类型void (*) ()function_ptr相关联。类型void (*) ()表示“指向一个函数的指针,该函数接受未指定数量的参数并返回void,”并且您可以使用任意数量的参数调用此类函数指针你想要的。这可能与您要查找的内容相符。

答案 1 :(得分:1)

在C语言中无法想到任何有意义的方式。(在C ++中,有一些解决方案可能符合用例。)主要原因是在没有任何东西的情况下知道某些东西是没有任何用处具体信息。 struct有一个名称,需要遵守ODR规则,因此有一个类型比较的概念,除了名称之外没有其他细节。

不透明的结构指针技术最好用于隐藏结构中的所有内容以及文件外部的内容。因此将函数指针放在结构中可能是最好的选择。这可能会增加额外的间接水平。

答案 2 :(得分:1)

一个丑陋的解决方法是在void (*voidfunc_ptr)(void)文件中声明任何有效函数指针类型的指针(例如,最简单的void返回0-arg函数,如api.h)然后在调用时将它们转换为实际类型。然后,您需要将实际的函数指针公开给api.h文件。你不能使用函数本身,因为它不是真正的类型(如果你在一个地方声明你的函数voidfunc并在其他地方使用它们的完整定义,它可能会起作用,但这是一个ODR违规)。

您可以这样做的一种方法是使用双重间接:在函数实现可见的文件中,为voidfunc全局分配一个函数指针。

这是草图:

// 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]);
}

所以你完全隐藏了api.h文件中函数指针的类型,但是一个代价是双重间接(你必须存储一个voidfunc_ptr*,这是一个指向函数的指针 - 指针)以便从extern voidfunc_ptr获得允许静态初始化。如果您不需要静态初始化,则可以避免双重间接。

另一个成本是一个丑陋的调用方:你需要将voidfunc_ptr强制转换回真正的底层指针类型。也许有一种方法可以通过声明另一个structS2那样的pu S来包含正确的指针类型(我不确定这是否合法)来将痛苦转移到其他地方。如果您忘记执行此操作,则可以将该函数称为voidfunc_ptr,可能会带来灾难性后果。

好处是你仍然可以在通话地点(演员表之后)获得参数类型检查。

相关问题