我对某些动态加载的代码有一个外部的“ C”接口。对于该代码,某些对象是不透明的句柄,表示为void*
。界面如下:
extern "C" {
void* get_foo() { Foo* foo = /* create and remember object */; return foo; }
int foo_get_bar(void* Foo) { return ((Foo*)foo)->bar(); }
}
这种模式几乎是跨C-API AFAIK与C ++对象进行交互的标准方法。但是我想知道是否可以忽略该指针转换吗?
将生成代码的调用者,并且仅将其链接到上面的接口(它具有自己的函数声明)。因此,它可以有效地生成如下代码:
void foo_get_bar(void*);
void* get_foo();
int do_something() { return foo_get_bar(get_foo()) };
我们可以假定调用者正确使用了代码(即,它没有传递错误的指针)。但是,当然它没有Foo
指针的概念。
现在我可以将界面更改为(简单的)变体吗
extern "C" {
int foo_get_bar(Foo* Foo) { return foo->bar(); }
}
或者void*
和Foo*
之间在链接器级别上有细微差别(例如,大小不匹配吗?)。
edit:我可能与C中的调用者代码引起了一些混淆。没有C代码。没有C编译器,因此没有C类型检查器。调用者代码已生成,并使用C-API。因此,要使用不透明的结构,我需要知道C-API在编译后如何表示该指针。
答案 0 :(得分:2)
我发现这个问题有点困惑,但是如果我理解正确的话,那很好。在任何现代平台上,指向任何事物的指针的大小都是相同的(幸运的是,我们已经__near
,__far
和__middling
的疯狂消失了。)
因此,我可能会在声明get_foo()
的头文件中引入类似类型安全的概念:
// Foo.h
struct Foo;
extern "C"
{
Foo* get_foo ();
int foo_get_bar (Foo* Foo);
}
在Foo
类(大概是C ++)的实现中,我们可能会:
// foo.cpp
struct Foo
{
int get_bar () { return 42; }
};
Foo* get_foo () { return new Foo (); }
int foo_get_bar (Foo *foo) { return foo->get_bar (); }
鬼S,但合法。您只需要在struct
中将Foo声明为class
而不是foo.cpp
,这意味着如果您想要事物private
或protected
,则必须明确这样说。
如果那不是可口的,那么您可以在foo.h中这样做:
#ifdef __cplusplus
class Foo;
#else
struct Foo;
#endif
然后将Foo
声明为class
,而不是struct
中的foo.c
。选择你的毒药。
答案 1 :(得分:1)
实际上,在普通平台x86和ARM上指针的大小/表示形式不应更改,因此您可能不愿意使用void *
。
但是,原则上,标准不保证引用类型不同的2个指针具有相同的指针表示和相同的宽度,除了 C11 6.2.5p28:
void
的指针与字符类型的指针具有相同的表示和对齐要求因此,用C表示指向C ++类的指针的最简单方法是使用指向不完整结构的指针:
typedef struct Foo Foo;
void foo_get_bar(Foo *);
Foo *get_foo(void);
另一个优点是,如果您尝试错误使用Bar *
,则编译器会抱怨。
P.S .:请注意,C中的get_foo()
does not mean the same as get_foo(void)