使用定义与内部使用的定义不同的外部 header 文件
Using external header file with definitions different from the ones used internally
假设您正在编写一个内部使用特定数据结构的库,并且只想向用户导出其中的一个子集(或者使用 void *
之类的东西隐藏确切的类型)。库中使用的所有结构和函数的定义都在 header library.h
中,将在构建库时使用。
生成另一个 library.h
的副本是否被认为是好的做法,该副本不会在构建过程中使用,而仅供链接到库的用户使用?
例如,假设库在内部使用以下 library.h
:
#ifndef LIBRARY_H
#define LIBRARY_H
struct myStruct {
int some_x;
void (*some_callback)(void);
};
typedef struct myStruct *myStruct_t;
#endif
虽然我们想对用户隐藏myStruct
的定义,所以我们导出一个headerlibrary.h
即:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef void *myStruct_t;
#endif
Is it considered good practice to also produce another copy of library.h that would not be used during the build process but only by users linking to the library?
没有。虽然关于你想做的事情的最佳实践的细节可能是一个品味问题,但在构建过程中交付未使用的 headers 客观上不是一个好的实践:你冒着引入输入错误的风险,而这些错误在你创建时从未被发现构建您的项目。
因此,无需详细说明您应该如何组织它,您绝对应该做的是让每个“private”header #include
各自的“public”header并且不在私有header中重复public声明。对于您的示例,这看起来例如喜欢:
library.h:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef struct myStruct *myStruct_t;
// there's absolutely no need to use void * here. An incomplete struct
// type is perfectly fine as long as only pointers to it are used.
#endif
library_internal.h:
#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"
struct myStruct {
int some_x;
void (*some_callback)(void);
};
#endif
其他 "best practice" 注释:
不要将指针隐藏在 typedef
之后。大多数 C 程序员都清楚指针是 声明符 的一部分,并期望在有指针时显式 see 指针。取消引用某些 看起来 不像指针的东西只会让其他阅读代码的人感到困惑。您还可能会混淆您的图书馆的消费者,让他们期望 myStruct_t
表现出 call-by-value 语义。
不要使用 _t
后缀定义您自己的类型。至少在 POSIX 中,这是为实现(compiler/runtime)保留的。定义一个与 struct
标签同名的类型没有错。
带有这些附加建议的示例:
library.h:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef struct myStruct myStruct;
#endif
library_internal.h:
#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"
struct myStruct {
int some_x;
void (*some_callback)(void);
};
#endif
请注意,C 标准不保证指向 void 的指针具有与指向结构的指针兼容的表示!因此:
typedef struct myStruct *myStruct_t;
typedef void *myStruct_t;
这两个不兼容,不能在严格符合的程序中使用。
另一件事是你通常不应该隐藏指针,除非需要。以标准库中的 FILE
为例。它的内容没有在任何地方定义,但是所有函数都专门 return 指向它的 指针 并接受指向它的 指针 。
您甚至可以使用简单的 struct
声明,而不是定义:
struct myStruct;
然后外部用户可以定义一个变量作为指向它的指针
struct myStruct *handle;
或者如果你想隐藏它确实是一个结构的事实,使用 typedef
:
typedef struct myStruct myStruct;
那么外部接口的用户可以简单地定义他们的变量
myStruct *handle;
假设您正在编写一个内部使用特定数据结构的库,并且只想向用户导出其中的一个子集(或者使用 void *
之类的东西隐藏确切的类型)。库中使用的所有结构和函数的定义都在 header library.h
中,将在构建库时使用。
生成另一个 library.h
的副本是否被认为是好的做法,该副本不会在构建过程中使用,而仅供链接到库的用户使用?
例如,假设库在内部使用以下 library.h
:
#ifndef LIBRARY_H
#define LIBRARY_H
struct myStruct {
int some_x;
void (*some_callback)(void);
};
typedef struct myStruct *myStruct_t;
#endif
虽然我们想对用户隐藏myStruct
的定义,所以我们导出一个headerlibrary.h
即:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef void *myStruct_t;
#endif
Is it considered good practice to also produce another copy of library.h that would not be used during the build process but only by users linking to the library?
没有。虽然关于你想做的事情的最佳实践的细节可能是一个品味问题,但在构建过程中交付未使用的 headers 客观上不是一个好的实践:你冒着引入输入错误的风险,而这些错误在你创建时从未被发现构建您的项目。
因此,无需详细说明您应该如何组织它,您绝对应该做的是让每个“private”header #include
各自的“public”header并且不在私有header中重复public声明。对于您的示例,这看起来例如喜欢:
library.h:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef struct myStruct *myStruct_t;
// there's absolutely no need to use void * here. An incomplete struct
// type is perfectly fine as long as only pointers to it are used.
#endif
library_internal.h:
#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"
struct myStruct {
int some_x;
void (*some_callback)(void);
};
#endif
其他 "best practice" 注释:
不要将指针隐藏在
typedef
之后。大多数 C 程序员都清楚指针是 声明符 的一部分,并期望在有指针时显式 see 指针。取消引用某些 看起来 不像指针的东西只会让其他阅读代码的人感到困惑。您还可能会混淆您的图书馆的消费者,让他们期望myStruct_t
表现出 call-by-value 语义。不要使用
_t
后缀定义您自己的类型。至少在 POSIX 中,这是为实现(compiler/runtime)保留的。定义一个与struct
标签同名的类型没有错。
带有这些附加建议的示例:
library.h:
#ifndef LIBRARY_H
#define LIBRARY_H
typedef struct myStruct myStruct;
#endif
library_internal.h:
#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"
struct myStruct {
int some_x;
void (*some_callback)(void);
};
#endif
请注意,C 标准不保证指向 void 的指针具有与指向结构的指针兼容的表示!因此:
typedef struct myStruct *myStruct_t;
typedef void *myStruct_t;
这两个不兼容,不能在严格符合的程序中使用。
另一件事是你通常不应该隐藏指针,除非需要。以标准库中的 FILE
为例。它的内容没有在任何地方定义,但是所有函数都专门 return 指向它的 指针 并接受指向它的 指针 。
您甚至可以使用简单的 struct
声明,而不是定义:
struct myStruct;
然后外部用户可以定义一个变量作为指向它的指针
struct myStruct *handle;
或者如果你想隐藏它确实是一个结构的事实,使用 typedef
:
typedef struct myStruct myStruct;
那么外部接口的用户可以简单地定义他们的变量
myStruct *handle;