如果你想切入追逐,请跳到最后两段。如果您对我的困境以及我采取的解决方案感兴趣,请继续直接阅读下面的内容。
我目前正在开发C库的一部分作为我实习的一部分。很自然地,有些代码部分不应该被用户访问而其他部分应该是可访问的。我基本上开发了几个架构优化的随机数生成器(RNG' s)(统一,高斯和指数分布数)。后两个RNG依赖于统一的生成器,它位于不同的内核(项目)中。因此,在用户想要使用多个RNG的情况下,我想确保我不会不必要地复制代码,因为我们受到内存的约束(没有必要在不同的地址多次定义相同的函数)在代码段中。)
现在问题就出现了。库中所有其他内核的约定是我们有两个头文件和两个C文件(一个用于自然C实现和优化的C版本(可能使用一些内部函数和程序集和/或有一些限制)使我们的架构变得更快更好。接下来是另一个C文件(一个测试平台),我们的主要功能位于其中,它测试两个实现并比较结果。话虽如此,我们无法真正添加额外的头文件私有或受保护的项目也不能为所有这些生成器添加全局头文件。
为了解决这个限制,我在C文件中使用了 extern 函数和 extern const int ,这些文件依赖于统一的RNG而不是 #define 位于每个C文件的顶部,以便使代码更易于移植,并且可以在一个位置轻松修改。这在很大程度上起作用。
然而,棘手的一点是我们在这些内核中使用内部类型(用户不应该看到它,不应该放在头文件中)。同样,为了便于移植,我希望能够在一个地方而不是在多个内核中的多个位置更改此 typedef 的定义,因为该库可能稍后用于另一个平台并且用于使用32位类型的算法至关重要。
所以我基本上想知道是否有任何方式可以制作 typedef " protected"在C.也就是说,我需要它在需要它的所有C文件中可见,但对用户不可见。它可以位于其中一个头文件中,但对于将在其项目中包含该头文件的用户,无论可能是什么,都不能看到它。
============================的 修改 ==== ============================
我还应该注意到我使用的typedef是unsigned int。所以
typedef unsigned int myType
不涉及任何结构。
============================ 超级编辑 === =======================
也禁止使用stdint.h :(
答案 0 :(得分:2)
只做
typedef struct foo foo;
这是两个声明,一个struct
的前向声明和一个具有相同名称的类型别名。前向声明的struct
只能用于定义指向它们的指针。这应该给你足够的抽象和类型安全。
在你的所有界面中
extern void proc(foo* a);
你必须提供功能
extern foo* foo_alloc(size_t n);
extern void foo_free(foo* a);
这会将您的用户和您的图书馆绑定为始终使用相同的struct
。因此,foo
的实现对API用户完全隐藏。您甚至可能有一天决定使用与struct
不同的内容,因为用户应使用foo
而不使用struct
关键字。
编辑:对某种整数只有typedef
对你没什么帮助,因为这些只是类型的别名。别名为unsigned
的所有类型都可以互换使用。解决这个问题的一种方法是将它们封装在struct
内。这会使你的内部代码有点难看,但生成的目标代码应与完美的现代编译器完全相同。
答案 1 :(得分:2)
我正在Jens Gustedt’s answer扩展,因为OP仍有疑问。
首先,不清楚为什么你有两个实现的单独头文件(“自然C”和“优化C”)。如果它们实现相同的API,则应该为一个标头服务。
Jens Gustedt建议您在标头中声明struct foo
,但仅在实现的C源文件中定义它,而不是在标头中定义它。以这种方式声明的struct
是一个不完整的类型,只能看到声明而不是定义的源代码无法看到类型中的内容。但是,它可以使用指向该类型的指针。
不完整struct
的声明可能与struct foo
一样简单。您还可以定义类型,例如typedef struct foo foo;
或typedef struct foo Mytype;
,并且可以定义指向struct
的指针类型,例如typedef struct foo *FooPointer;
。然而,这些仅仅是为了方便。它们不会改变基本概念,即API用户无法看到struct foo
,但他们可以指向。{/ p>
在实施中,您将完全定义struct
。如果您想在unsigned int
中使用struct
,则可以使用:
struct foo
{
unsigned int x;
};
通常,您定义struct foo
以包含您喜欢的任何数据。
由于API用户无法定义struct foo
,因此必须提供用于根据需要创建和销毁此类对象的函数。因此,您可能会将一个函数声明为extern struct foo *FooAlloc(some parameters);
。该函数创建一个struct foo
对象(可能通过调用malloc
或相关函数),使用参数中的数据初始化它,并返回指向该对象的指针(如果创建或初始化失败,则返回NULL) 。您还可以使用函数extern void FooFree(struct foo *p);
释放struct foo
对象。您可能还具有重置,设置或更改foo对象状态的函数,复制foo对象的函数以及报告foo对象的函数。
您的实现还可以定义一些全局struct foo
对象,这些对象可以对API用户可见(实际上仅通过地址)。作为一个好的设计,这应该只为某些特殊目的而做,例如提供具有特殊含义的struct foo
个对象的实例,例如具有永久“初始状态”的常量对象进行复制。
您的两个实现,“自然C”和“优化C”实现可能对struct foo
有不同的定义,前提是它们不是同时在程序中使用。 (也就是说,每个整个程序都是用一个实现编译的,而不是两个。如果有必要,你可以通过使用union将两者都编入程序,但最好避免这种情况。)
这不是单身方法。