我正在从 struct 类型中隐藏一些结构字段,以使公共API标头更加清晰。
首先,我使用这种样式隐藏(实际上没有隐藏,只是与公共可编辑成员分开),
include/scene.h
typedef struct ScenePrivateFields {
SceneFlags flags;
/* other members */
} ScenePrivateFields;
typedef struct Scene {
ScenePrivateFields _priv;
RootNode rootNode;
/* other members */
} Scene;
使用此样式,我始终使用场景类型在功能参数中,然后使用 _PRIV 成员访问私有字段。但是将所有私人字段保留在公共标题中使标题更加复杂
最终我切换到另一种样式
include/scene.h
typedef struct Scene {
RootNode rootNode;
/* other members */
} Scene;
EXPORT
Scene*
allocScene(void);
src/types/impl_scene.h
typedef struct SceneImpl {
Scene pub;
SceneFlags flags;
/* other members */
} SceneImpl;
例如,如果我有这样的函数:
include/scene.h
void
renderScene(Scene * __restrict scene, /* other params */);
我必须将场景投放到场景Impl才能访问私人字段。我正在这样做:
src/scene/scene.c
void
renderScene(Scene * __restrict scene, /* other params */) {
SceneImpl *sceneImpl;
sceneImpl = (SceneImpl *)scene;
}
避免施放所有功能,我认为也许我可以做这样的事情,如果它是合法的,而不是违反C标准:
src/scene/scene.c
void
renderScene(SceneImpl * __restrict sceneImpl, /* other params */) {
/* skip casting scene to sceneImpl */
}
由于场景是 sceneImpl 的第一个成员,我可以用场景定义public api(函数),并用sceneImpl定义实现(函数)。我认为这会因为两个指针,有效还是好主意,这会起作用吗?
注意:我正在使用-fstrict -aliasing
编译edit :fwiw,在此处,Alloc函数实现,用户必须将此func使用到Alloc struct:
EXPORT
Scene*
allocScene(void) {
SceneImpl *sceneImpl;
sceneImpl = calloc(1, sizeof(*sceneImpl));
/* initialize pulic part */
sceneImpl->pub.field1 = ...
/* initialize private part */
sceneImpl->priv_field1 = ...
/* return public part */
return &sceneImpl->pub;
}
您可以将不透明类型用于数据的私人药水。
在您的公共标题中,定义您的结构如下:
// forward declaration of struct ScenePrivateFields
typedef struct ScenePrivateFields ScenePrivateFields;
typedef struct Scene {
ScenePrivateFields *_priv; // now a pointer
RootNode rootNode;
/* other members */
} Scene;
然后在您的实现文件中定义私人结构:
struct ScenePrivateFields {
SceneFlags flags;
/* other members */
}
您还需要定义一个动态创建struct ScenePrivateFields
实例的函数,然后返回指针以填充公共结构中的指针。
丹尼斯·里奇(Dennis Ritchie)设计和称为C的语言在物理存储布局方面定义了许多行为。这使Ritchie的语言优于系统编程等其他许多现有语言,因为这意味着该语言可用于实施其创建者所想象的数据结构。
。编译器为支持Dennis Ritchie语言的语义做出了真正的努力,那么它在支持您描述的结构时应该毫无困难。不幸的是,当编写(根据已发表的理由)编写规则时,应该允许编译器在大多数情况下进行某些优化,在大多数情况下,没有理由期望他们会更改程序的语义编译器作家认为这是一种邀请,以忽略这种优化会改变程序语义的邀请。
如果不需要"用户"代码查看完整的结构类型,则可以使用一个标题文件说typedef struct tagName typeName;
,然后使用typeName*
类型的通用指针。不幸的是,即使用户代码不需要实际访问大多数成员,有时也可能有必要执行需要全类型的事情。如果有必要的话,我建议最好使用任何编译器选项(s),以便告诉编译器,应该允许一种可能使用一种结构类型的指针来访问另一种结构的成员与编译器作家渴望处理具有如此有用能力的语言的态度相比,具有合适的布局[-fno-strict-aliasing
]。