【问题标题】:Is there any way I can make "protected" typedef's in C?有什么办法可以在 C 中制作“受保护的”typedef?
【发布时间】:2013-07-10 08:38:01
【问题描述】:

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

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

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

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

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

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

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

typedef unsigned int myType

不涉及结构。

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

【问题讨论】:

  • typedef struct foo { int bar; } foo; in <inernal.h>,然后只向用户公开<non-internal.h>
  • 但同样,我不能真正向内核添加另一个头文件(由于历史原因与库的其余部分)。否则这整个崩溃会更容易。对我来说很不幸(对他来说很幸运),主管正在欧洲度假,所以我真的不能问他是否可以更改文件结构。内部类型本身就引发了一些关于历史实践的争论。最终,决定只在内部使用它,现在我坐在这里:像地毯上的虫子一样自鸣得意;没有骰子!

标签: c types typedef protected information-hiding


【解决方案1】:

我正在扩展 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,但他们可以拥有指向的指针。

在实现内部,您将完全定义struct。如果你想在struct 中添加一个unsigned int,你可以使用:

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 有不同的定义,前提是它们没有在一个程序中一起使用。 (也就是说,每个完整的程序都是用一个实现编译的,而不是两者都编译。如有必要,您可以使用联合将两者合并到一个程序中,但最好避免这种情况。)

这不是单例方法。

【讨论】:

  • 我喜欢扩展的想法。
【解决方案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 的东西,因为用户应该使用不带struct 关键字的foo

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

【讨论】:

  • 我是否可以使用我在编辑中提到的 unsigned int typedef 来执行此操作(抱歉,编辑速度不够快,无法回复您的回复)?
  • 另外,如果您建议使用单例方法,那并不是我想要的,因为我需要多个无符号 32 位数字(也禁止使用 stdint.h)。我只需要我的所有 C 文件都可以访问类型本身。
猜你喜欢
  • 2019-07-24
  • 1970-01-01
  • 2015-11-30
  • 2011-01-06
  • 2014-10-09
  • 2011-12-05
  • 2015-02-25
  • 1970-01-01
  • 2020-02-23
相关资源
最近更新 更多