【问题标题】:opaque (abstract) data types in CC中的不透明(抽象)数据类型
【发布时间】:2010-01-04 19:36:09
【问题描述】:

文件 api.h

#include <stdio.h>
#ifndef API
#define API

struct trytag;
typedef struct trytag try;

void trial (try *);

#endif

文件 core.h

#ifndef CORE
#define CORE
struct trytag
{
    int a;
    int b;
};
#endif

文件 func.c

#include "api.h"
#include "core.h"

void trial (try *tryvar)
{
    tryvar->a = 1;
    tryvar->b = 2;
}

文件 main.c

#include "api.h"

int main ()
{
    try s_tryvar;

    trial(&s_tryvar);

    printf("a = %d\nb = %d\n", s_tryvar.a, s_tryvar.b);
}

当我编译时,我得到:

main.c:5: error: storage size of ‘s_tryvar’ isn’t known

如果我在main.c 中包含core.h,则不会出现此错误,因为在core.h 中定义了try。但我希望结构 try 隐藏到 main.c — 它不应该知道 try 结构的成员。我错过了什么?

【问题讨论】:

    标签: c types abstract


    【解决方案1】:

    我认为您尝试做的事情是不可能的。编译器需要知道try 结构有多大才能编译main.c。如果你真的希望它是不透明的,就创建一个通用指针类型,而不是直接在main() 中声明变量,而是使用alloc_try()free_try() 函数来处理创建和删除。

    类似这样的:

    api.h:

    #ifndef API
    #define API
    
    struct trytag;
    typedef struct trytag try;
    
    try *alloc_try(void);
    void free_try(try *);
    int try_a(try *);
    int try_b(try *);
    void trial (try *);
    
    #endif
    

    core.h:

    #ifndef CORE
    #define CORE
    struct trytag
    {
        int a;
        int b;
    };
    #endif
    

    func.c:

    #include "api.h"
    #include "core.h"
    #include <stdlib.h>
    
    try *alloc_try(void)
    {
        return malloc(sizeof(struct trytag));
    }
    
    void free_try(try *t)
    {
        free(t);
    }
    
    int try_a(try *t)
    {
        return t->a;
    }
    
    int try_b(try *t)
    {
        return t->b;
    }
    
    void trial(try *t)
    {
        t->a = 1;
        t->b = 2;
    }
    

    main.c:

    #include <stdio.h>
    #include "api.h"
    
    int main()
    {
        try *s_tryvar = alloc_try();
    
        trial(s_tryvar);
        printf("a = %d\nb = %d\n", try_a(s_tryvar), try_b(s_tryvar));
    
        free_try(s_tryvar);
    }
    

    【讨论】:

    • 糟糕,不要执行“typedef void try;”。当你这样做时,你会失去类型安全(编译器将无法捕捉到诸如int x; trial(&amp;x); 之类的废话。引入新结构的问题技术要好得多。
    • 关于分配实例的部分是正确的,但我想知道当 main.c 只有 api.h 而不是 core.h 时,它应该如何取消引用 a 和 b。如果没有 func.c 公开一些访问辅助方法,我认为这将永远无法工作。
    • 为了真正的安全,您可能希望在alloc_try() 函数中分配和填充一些标识字段。这样你就可以检查你传入的结构是否有效。
    • @Steven Sudit - 是的。我们不能在 printf 中取消引用 s_tryvar。它应该被评论。
    • @Carl Norum:是的,这是个好主意。如果开销有问题,可以在不处于调试模式时编译出来。如果这是 C++,我建议改用 dynamic_cast。
    【解决方案2】:

    想想不透明的 FILE 结构在 C 中是如何工作的。您只使用指针,并且需要像 fopen() 这样的函数来创建实例,并需要像 fclose() 这样的函数来处理它。

    【讨论】:

      【解决方案3】:

      问题出在main.c中,编译器没有看到struct try的定义。因此,编译器仅限于使用指向 struct try 的指针。

      您要做的是向您的 API 添加两个新函数:

      try *create_try();
      void *destroy_try(try *t);
      

      这些函数将分别调用 malloc 和 free。

      如果您不想将结构限制为仅允许在堆上使用,那么您将不得不放弃使其不透明。

      【讨论】:

      • 或者通过返回一个包含不透明指针的非透明结构来隐藏它在堆上的事实。
      【解决方案4】:

      有一种方法可以做一些技术上并不完全符合您要求的事情,但应该服务于保持结构不透明同时支持非堆分配的相同目的。

      在 api.h 中,您声明一个不透明的结构如下:

      struct trytag_opaque
      {
          char data[sizeof(int)*2];
      };
      

      如果您想要更不透明,您可以计算跨任何受支持平台所需的结构的最大尺寸,并使用:

      struct trytag_opaque
      {
          char data[MAX_TRYTAG_SIZE];
      };
      

      那么您的 api.h 函数声明将如下所示:

      int try_a(struct trytag_opaque *t)
      

      您的功能代码如下所示:

      int try_a(struct trytag_opaque *t_opaque) {
          trytag *t = (trytag *)t_opaque;
          ...
      }
      

      你的 main.c 看起来像:

      #include "api.h"
      int main() {
          struct trytag_opaque t;
          ...
          try_a(&t);
          ...
      }
      

      【讨论】:

      • 为了进一步定制解决方案,您可以有一个帮助程序将私有结构的大小打印到标准输出,然后将帮助函数输出通过管道传递到包含在头文件中的代码 sn-p这定义了必要的大小。 sn -p 的生成都可以在 Makefile 中进行管理。一开始设置有点麻烦——可能需要 15 分钟——但可重复用于所有私有结构,并且大小始终适合给定平台
      • 顺便说一句,帮助程序方法仅在您在每个平台上进行本机编译时才有效。不太适合交叉编译
      猜你喜欢
      • 1970-01-01
      • 2015-02-23
      • 2013-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-12
      • 1970-01-01
      • 2021-12-09
      相关资源
      最近更新 更多