3

如果你想切入正题,请跳到最后两段。如果您对我的困境以及我为解决它所采取的步骤感兴趣,请继续阅读下面的内容。

作为实习的一部分,我目前正在开发 C 库的一部分。所以很自然,有些代码部分不应该被用户访问,而其他部分应该是。我基本上正在开发几个架构优化的随机数生成器 (RNG)(均匀、高斯和指数分布数)。后两个 RNG 依赖于 uniform generator ,它位于不同的内核(项目)中。所以,如果用户想要使用多个 RNG,我想确保我不会不必要地复制代码,因为我们受到内存的限制(在不同的地址多次定义相同的函数是没有意义的代码段)。

现在问题出现了。库中所有其他内核的约定是我们有两个头文件和两个 C 文件(每个用于自然 C 实现和优化的 C 版本(可能使用一些内在函数和汇编和/或有一些限制)使其更快更好地适应我们的架构)。紧随其后的是另一个 C 文件(测试平台),我们的 main 函数位于该文件中,它测试两个实现并比较结果。话虽如此,我们不能真正添加​​额外的头文件私有或受保护的项目,我们也不能为所有这些生成器添加全局头文件。

为了克服这个限制,我在 C 文件中使用了extern函数和extern const int,它们依赖于统一的 RNG 而不是每个 C 文件顶部的#define,以使代码更易于移植和修改一个地方。这在大多数情况下都有效。

然而,棘手的一点是我们在这些内核中使用了内部类型(用户不应该看到,也不应该放在头文件中)。同样,为了可移植性,我希望能够在一个地方而不是在多个内核中的多个地方更改此typedef的定义,因为该库以后可能会用于另一个平台,并且对于算法的工作而言,至关重要的是我使用 32 位类型。

所以基本上我想知道是否有任何方法可以让typedef在 C 中“受保护”。也就是说,我需要它在所有需要它的 C 文件中可见,但对用户不可见。它可以在其中一个头文件中,但对于将在他/她的项目中包含该头文件的用户,无论它可能是什么,都不能看到它。

=============================编辑===================== ===========
我还应该注意我使用的 typedef 是一个无符号整数。所以

typedef unsigned int myType

不涉及结构。

============================超级编辑==================== ======
也禁止使用 stdint.h :(

4

2 回答 2

2

由于 OP 仍有问题,我正在扩展Jens Gustedt 的回答。

首先,不清楚为什么您有两个实现(“自然 C”和“优化 C”)的单独头文件。如果它们实现相同的 API,则应为其中任何一个提供一个标头。

Jens Gustedt 的建议是在标头中声明 a struct foo,但仅在实现的 C 源文件中而不是在标头中定义它。以这种方式声明的Astruct是不完整的类型,并且只能看到声明而不是定义的源代码无法看到类型中的内容。但是,它可以使用指向该类型的指针。

不完整的声明struct可能像struct foo. 您还可以定义一个类型,例如typedef struct foo foo;or typedef struct foo Mytype;,并且您可以定义一个指向 的类型struct,例如typedef struct foo *FooPointer;. 然而,这些仅仅是为了方便。它们不会改变基本概念,struct foo即 API 用户无法看到但他们可以有指针指向的基本概念。

在实现内部,您将完全定义struct. 如果你想要一个unsigned intin 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. (也就是说,每个完整的程序都是用一个实现编译的,而不是两者都编译。如有必要,您可以使用联合将两者合并到一个程序中,但最好避免这种情况。)

这不是单例方法。

于 2013-07-11T23:38:33.213 回答
2

做就是了

typedef struct foo foo;

这是两个声明,一个前向声明 astruct和一个同名的类型别名。前向声明struct只能用于定义指向它们的指针。这应该给你足够的抽象和类型安全。

在您拥有的所有界面中

extern void proc(foo* a);

你必须提供功能

extern foo* foo_alloc(size_t n);
extern void foo_free(foo* a);

这将绑定您的用户以及您的库以始终使用相同的struct. 从而对fooAPI 用户完全隐藏了实现。您甚至可以有一天决定使用与 a 不同的东西,struct因为用户应该foo在没有struct关键字的情况下使用。

编辑:只是typedef某种整数对你没有多大帮助,因为这些只是类型的别名。您所有别名为的类型unsigned都可以互换使用。解决此问题的一种方法是将它们封装在struct. 这会使你的内部代码有点难看,但生成的目标代码应该与一个好的现代编译器完全相同。

于 2013-07-11T21:55:43.007 回答