别名结构(或将一个到另一个的粘贴定义)

时间:2017-08-13 09:50:06

标签: c struct typedef

我想创建一个API,用于以不透明的方式设置和获取结构的字段(客户端应该只处理它们的指针并将它们传递给头文件中声明的方法)。标准的东西,你在库的源文件中定义你的结构,并做

typedef struct __internal_struct shiny_new_opaque_type;

问题是,目前,该类只是一个已经存在的API的包装器(很快就会改变)。所以我需要使用的结构是在其他头文件中定义的(完整的结构声明就在那里,我想要从我的客户端隐藏,所以任何取消引用指针和访问结构成员的尝试都会导致编译器错误)。因此,我不想在我的标题中包含这些标题(仅在.c文件中)。我看到了三种可能的处理方法。

  1. 而不是

    typedef struct __internal_struct shiny_new_opaque_type;
    

    DO

    typedef void shiny_new_opaque_type;
    

    让我的方法做指针转换。这很危险,因为编译器无法进行类型检查。

  2. 复制粘贴我正在使用的新struct __internal_struct下的结构定义(最终我必须定义自己的结构)。也许这是最好的选择?

  3. 现在定义我的__internal_struct以包含一个成员,该成员是我正在使用的其他API的相应结构并使用它。有点难看......

  4. 基本上有一种方法可以将一个结构输入到另一个结构中,或者使用已经定义的结构作为另一个结构中的匿名成员,这样在一天结束时两个结构都是等价的吗?以下两种方法均无效:

    typedef struct transparent_struct struct __internal_struct;
    struct __internal_struct
    {
          struct transparent; // anonymous, direct access to its members
    }
    

    修改 从评论来看,在我看来,3或其变体将是最佳选择。正如@Akira所指出的,还有可能永远不会定义我的结构。所以

    1. 标题:typedef struct my_type; // never defined
    2. 在我的源代码中总是使用强制转换(struct transparent *)my_type_ptr

      1. 标题:typedef struct _internal_struct my_type;
      2. 在源文件中:

        struct _internal_struct {
            struct transparent t;
        }
        

        然后我可以选择其中一个:

        • my_type_ptr->t.member
        • ((struct transparent*)my_type_ptr)->member

2 个答案:

答案 0 :(得分:3)

如果您打算使用opaque pointers,则应仅向用户提供不完整 struct类型,并让他们通过提供的功能执行操作,其中一个参数是指向不完整的struct类型的指针。

例如,我们认为我们有一个API,它提供struct foo类型和void print(struct foo*)函数来打印struct foo个实例的内容。包装器可以按如下方式实现:

<强> wrapper.h

#ifndef WRAPPER_H
#define WRAPPER_H

struct my_obj; /* incomplete type, but you can use pointers to it */

struct my_obj* create(void); /* creates new struct my_obj instance */
void destroy(struct my_obj*); /* deletes the pointed struct my_obj instance */

void set_name_and_id(struct my_obj*, const char*, unsigned);
void show(struct my_obj*);

#endif /* WRAPPER_H */

<强> wrapper.c

#include "wrapper.h"
#include "api.h" /* API only included here */

#include <stdlib.h>
#include <string.h>

#define TO_FOO(my_ptr) ((struct foo*)my_ptr)

struct my_obj* create(void) {
    return calloc(1, sizeof(struct foo)); /* allocates memory for 'struct foo' */
}

void destroy(struct my_obj* obj) {
    free(obj);
}

void set_name_and_id(struct my_obj* obj, const char* name, unsigned id) {
    strcpy(TO_FOO(obj)->bar, name);
    TO_FOO(obj)->baz = id;
}

void show(struct my_obj* obj) {
    print(TO_FOO(obj)); /* accepts only 'struct foo' pointers */
}

Live Demo

当用户在上面的示例中包含wrapper.h时,他们无法看到api.h并且无法取消引用指向struct my_obj的指针,因为它和&internal_struct #39;是一个不完整的类型。

回复您的评论:

  

在这种情况下,internal_struct->api.member和api都具有相同的大小,已对齐,我可以使用((struct API*)internal_struct)->memberinternal_struct->api.member访问api的成员。您对这两个选项的看法是什么?

根据N1570草稿():

6.7.2.1结构和联合说明符

  

15(...)指向结构对象的指针,适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单位),,反之亦然。在结构对象中可能有未命名的填充,但不是在它的开头。

所以,你的两种方法都是好的和安全的,它取决于你喜欢哪一种。使用formControlName="surveyType"很明显,我会使用这个版本。

答案 1 :(得分:1)

将评论转换为答案。

避免选项1 - 由于缺少类型安全性,API不应使用void指针。 C很糟糕;不要在通过什么类型的安全可用的情况下开孔。如果接口类型为struct SomeThing *,则可以将void *传递给函数,而无需从C编译器中调用,但是您无法将struct SomeThingElse *传递给函数(没有强制转换,但是需要添加一个演员应该在你的脑海中提出警告标志)。如果API使用void *,您可以将任何指针类型传递给函数而不进行任何强制转换或警告;这是非常不受欢迎的。

如果不是噩梦,选项2是维护责任。不要去那里。

因此,选项3是要走的路。你有两个子选项。

  • 3A - 您的结构只包含一个指向API结构类型(struct internal_struct { struct API *api; })的指针的成员,并且
  • 3B - 您的结构只包含一个API结构类型(struct internal_struct { struct API api; })的成员 - 区别在于*的存在与否。

3A和3B都工作;哪个更适合你,取决于你正在使用的API的组织 - 它是否接近不透明它对待它的结构类型。结构类型越不透明,3A越合适。另一方面,它在访问数据时会产生一些开销。

  

事实上,我最终选择了选项3B。在这种情况下,internal_struct和api都具有相同的大小,对齐,我可以使用internal_struct->api.member((struct API*)interna_struct)->member访问api的成员。您对这两个选项有何看法?

虽然演员的版本有效,但它很糟糕。无论何时你都可以避免演员阵容 - 他们是一个告诉编译器的大棒“我比你所做的更了解”。我尽可能避免演员表演。是的,我有时会使用演员;这几乎是不可避免的。但是我尽可能避免使用它们,这是一个非常可能的情况。

使用选项3B,编译器可能会为internal_struct->api.member((struct API *)internal_struct)->member生成相同的代码。所以,使用更清洁的符号 - 这也更简洁。重复api.会有轻微的麻烦;添加括号并重复struct API *会产生更大的麻烦。

如果你做了一次演员:

struct API *api_ptr = (struct API *)internal_struct;

然后在整个过程中使用api_ptr->member等,这可能是明智的,但无演员版仍然会更好。

struct API *api_ptr = &internal_struct->api;