【问题标题】:Hide type definition in C在 C 中隐藏类型定义
【发布时间】:2012-03-05 09:30:32
【问题描述】:

我有一个 .c 文件(一个函数库),其中包含一个函数和如下定义:

typedef long double big;
big foo(int x) { ... }

我想为这个库创建一个接口,.h。所以我这样做:

typedef long double big;
big foo(int);

并从.c 文件中删除typedef long double big;。但是通过这样做,我在我的界面中放弃了big 的类型定义,所以它并不是一个真正干净的界面。任何想法如何解决这个问题?

我知道我可以在我的 .c 文件中执行此操作:

struct foo {
  long double;
};

然后在.h 文件中执行:

typedef struct foo big;
big foo(int);

但是为一个字段创建struct 似乎很浪费,而且每当我想读取big 时,我都应该使用. 运算符。

【问题讨论】:

    标签: c types interface struct typedef


    【解决方案1】:

    如果该类型永远不会变得比long double 更复杂,那么可能不值得更多地隐藏它。如果它可能需要变得更复杂,那么您可以考虑使用不透明类型。在您的公共标头big.h 中,您使用:

    #ifndef BIG_H_INCLUDED
    #define BIG_H_INCLUDED
    typedef struct big big_t;
    
    extern big_t *foo(int);
    
    ...
    
    #endif
    

    所有函数都将获取并返回指向big_t 类型的指针。这就是你可以用这样的不完整类型做的所有事情。请注意,您的客户不能为自己分配任何 big_t 值;他们不知道字体有多大。这意味着您最终可能会得到以下功能:

    extern big_t *big_create(void);
    extern void   big_destroy(big_t *value);
    

    创建和销毁big_t 值。然后他们将能够用以下方法进行算术运算:

    extern big_errno_t big_add(const big_t *lhs, const big_t *rhs, big_t *result);
    

    等等。但是因为它们只有一个不透明的、不完整的类型,所以它们不能可靠地在 big_t 结构中乱搞。但请注意,您只能在界面中使用指针。传递或返回值需要完整的类型,如果类型完整,用户可以研究其内部工作原理。

    在实现标头bigimpl.h 中,您将拥有:

    #ifndef BIGIMPL_H_INCLUDED
    #define BIGIMPL_H_INCLUDED
    #include "big.h"
    
    struct big
    {
        ...whatever the details actually are...
    };
    
    #endif
    

    您的实现代码将只包含bigimpl.h,但其中包含big.h。这里的主要问题是确保您知道如何处理内存分配。

    有时这种技术是值得的。通常这并不是真正必要的。您需要自己进行评估。

    【讨论】:

    • 我真的很想避免使用struct,正如我在我的问题中所写的那样,还有其他方法吗?
    • 不,我不相信有替代方案 - 除非您将 union 视为 struct 的替代方案。嗯,有void * 的旧后备,但这比不完整的结构类型要糟糕得多。如果你真的想变得可怕,你可以让用户认为'哦,这是一个结构类型',而在你的代码中,你可以long double *my_value = (long double *)big_t_pointer_from_interface;。如果您想要更好的抽象,请使用 C++,但如果您这样做,您将在 C++ 中定义一个类,所以我们回到原点 - 不,除了定义一个结构之外,没有真正的替代方案。
    • 不透明(结构)类型的优点之一是它提供了比使用void *更好的安全边际;如果用户传递指向int 的指针或指向double 的指针,您的编译器会报错,如果big_t 是不透明类型,则需要big_t *。如果big_t *void * 的别名,那么您可以将任何旧指针传递给函数。
    【解决方案2】:

    并删除 typedef long double big;从 .c 文件。但是这样做我在我的接口中放弃了 big 的类型定义,所以它并不是一个真正干净的接口。

    怎么会?谁在乎你的代码的用户是否可以看到 typedef?怎么疼……什么?他们需要一种方法来使用您的 typedef,这就是它的完成方式。对我来说,这听起来像是一个没有明显不良影响的理论问题。你在担心错误的事情。

    但是 tit 似乎只为一个字段创建一个结构是浪费的,而且我应该使用 .每当我想阅读大篇幅时都可以使用运算符。

    是的,这很愚蠢。另外,现在您已经放弃了结构的定义!不好了!这与公开 typedef(或任何其他类型)有何不同?

    【讨论】:

    • 我在乎!如果用户看到long double,也许他开始使用long double 而不是big,比方说,一段时间后我想将big 更改为char *。然后用户的代码将无法工作。说typedef struct foo big;我不泄露我struct的定义,用户不知道struct foo的字段是什么。
    • 不过,这不是你的问题。这是用户的问题,因为他们拒绝使用您的 API。
    • @DavidBrown 我不认为这是一个有效的论点。例如。您可以将 Java 类中的所有字段定义为 public,然后对用户说不要使用字段,这似乎不是创建 API 的正确方法(放弃 b>里面它的信息)。
    • @FookoR.:你是对的,所有那些多年来一直这样做的图书馆设计师都是错的。正确的?您的类比不正确,因为这在 Java 中只是不好的做法,而且很容易避免。看,你无法以实际的方式解决这个问题。编写库并不意味着您在每一步都溺爱您的用户。您尽最大努力提供一个干净的 API,并且对于可能更改的类型的 typedef 是完全可以接受的。如果您的用户决定在任何地方都写long double,那就是他们的问题。
    • all those library designers who have been doing this for years are all wrong. 如果他们在所有领域都做public,我想他们是。
    猜你喜欢
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    • 2019-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多