我想创建一个API,用于以不透明的方式设置和获取结构的字段(客户端应该只处理它们的指针并将它们传递给头文件中声明的方法)。标准的东西,你在库的源文件中定义你的结构,并做
typedef struct __internal_struct shiny_new_opaque_type;
问题是,目前,该类只是一个已经存在的API的包装器(很快就会改变)。所以我需要使用的结构是在其他头文件中定义的(完整的结构声明就在那里,我想要从我的客户端隐藏,所以任何取消引用指针和访问结构成员的尝试都会导致编译器错误)。因此,我不想在我的标题中包含这些标题(仅在.c文件中)。我看到了三种可能的处理方法。
而不是
typedef struct __internal_struct shiny_new_opaque_type;
DO
typedef void shiny_new_opaque_type;
让我的方法做指针转换。这很危险,因为编译器无法进行类型检查。
复制粘贴我正在使用的新struct __internal_struct
下的结构定义(最终我必须定义自己的结构)。也许这是最好的选择?
现在定义我的__internal_struct
以包含一个成员,该成员是我正在使用的其他API的相应结构并使用它。有点难看......
基本上有一种方法可以将一个结构输入到另一个结构中,或者使用已经定义的结构作为另一个结构中的匿名成员,这样在一天结束时两个结构都是等价的吗?以下两种方法均无效:
typedef struct transparent_struct struct __internal_struct;
struct __internal_struct
{
struct transparent; // anonymous, direct access to its members
}
修改 从评论来看,在我看来,3或其变体将是最佳选择。正如@Akira所指出的,还有可能永远不会定义我的结构。所以
typedef struct my_type; // never defined
在我的源代码中总是使用强制转换(struct transparent *)my_type_ptr
typedef struct _internal_struct my_type;
在源文件中:
struct _internal_struct {
struct transparent t;
}
然后我可以选择其中一个:
my_type_ptr->t.member
((struct transparent*)my_type_ptr)->member
答案 0 :(得分:3)
如果您打算使用opaque pointers,则应仅向用户提供不完整 struct
类型,并让他们通过提供的功能执行操作,其中一个参数是指向不完整的struct
类型的指针。
例如,我们认为我们有一个API,它提供struct foo
类型和void print(struct foo*)
函数来打印struct foo
个实例的内容。包装器可以按如下方式实现:
<强> wrapper.h 强>
#ifndef WRAPPER_H
#define WRAPPER_H
struct my_obj; /* incomplete type, but you can use pointers to it */
struct my_obj* create(void); /* creates new struct my_obj instance */
void destroy(struct my_obj*); /* deletes the pointed struct my_obj instance */
void set_name_and_id(struct my_obj*, const char*, unsigned);
void show(struct my_obj*);
#endif /* WRAPPER_H */
<强> wrapper.c 强>
#include "wrapper.h"
#include "api.h" /* API only included here */
#include <stdlib.h>
#include <string.h>
#define TO_FOO(my_ptr) ((struct foo*)my_ptr)
struct my_obj* create(void) {
return calloc(1, sizeof(struct foo)); /* allocates memory for 'struct foo' */
}
void destroy(struct my_obj* obj) {
free(obj);
}
void set_name_and_id(struct my_obj* obj, const char* name, unsigned id) {
strcpy(TO_FOO(obj)->bar, name);
TO_FOO(obj)->baz = id;
}
void show(struct my_obj* obj) {
print(TO_FOO(obj)); /* accepts only 'struct foo' pointers */
}
当用户在上面的示例中包含wrapper.h
时,他们无法看到api.h
并且无法取消引用指向struct my_obj
的指针,因为它和&internal_struct
#39;是一个不完整的类型。
回复您的评论:
在这种情况下,
internal_struct->api.member
和api都具有相同的大小,已对齐,我可以使用((struct API*)internal_struct)->member
或internal_struct->api.member
访问api的成员。您对这两个选项的看法是什么?
6.7.2.1结构和联合说明符
15(...)指向结构对象的指针,适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单位),,反之亦然。在结构对象中可能有未命名的填充,但不是在它的开头。
所以,你的两种方法都是好的和安全的,它取决于你喜欢哪一种。使用formControlName="surveyType"
很明显,我会使用这个版本。
答案 1 :(得分:1)
将评论转换为答案。
避免选项1 - 由于缺少类型安全性,API不应使用void指针。 C很糟糕;不要在通过什么类型的安全可用的情况下开孔。如果接口类型为struct SomeThing *
,则可以将void *
传递给函数,而无需从C编译器中调用,但是您无法将struct SomeThingElse *
传递给函数(没有强制转换,但是需要添加一个演员应该在你的脑海中提出警告标志)。如果API使用void *
,您可以将任何指针类型传递给函数而不进行任何强制转换或警告;这是非常不受欢迎的。
如果不是噩梦,选项2是维护责任。不要去那里。
因此,选项3是要走的路。你有两个子选项。
struct internal_struct { struct API *api; }
)的指针的成员,并且struct internal_struct { struct API api; }
)的成员 - 区别在于*
的存在与否。3A和3B都工作;哪个更适合你,取决于你正在使用的API的组织 - 它是否接近不透明它对待它的结构类型。结构类型越不透明,3A越合适。另一方面,它在访问数据时会产生一些开销。
事实上,我最终选择了选项3B。在这种情况下,
internal_struct
和api都具有相同的大小,对齐,我可以使用internal_struct->api.member
或((struct API*)interna_struct)->member
访问api的成员。您对这两个选项有何看法?
虽然演员的版本有效,但它很糟糕。无论何时你都可以避免演员阵容 - 他们是一个告诉编译器的大棒“我比你所做的更了解”。我尽可能避免演员表演。是的,我有时会使用演员;这几乎是不可避免的。但是我尽可能避免使用它们,这是一个非常可能的情况。
使用选项3B,编译器可能会为internal_struct->api.member
和((struct API *)internal_struct)->member
生成相同的代码。所以,使用更清洁的符号 - 这也更简洁。重复api.
会有轻微的麻烦;添加括号并重复struct API *
会产生更大的麻烦。
如果你做了一次演员:
struct API *api_ptr = (struct API *)internal_struct;
然后在整个过程中使用api_ptr->member
等,这可能是明智的,但无演员版仍然会更好。
struct API *api_ptr = &internal_struct->api;