某些配置中不需要的 C 类型的良好实践
Good practice for types in C which are not needed in certain configurations
我正在为C 项目中的线程相关函数编写一个抽象层。并非所有可以为其构建代码的平台都支持多线程;这些平台有替代品。
如果支持多线程,头文件会定义一个预处理器宏,因此预处理器条件可用于启用和禁用仅在多线程(或不在多线程时)才需要的代码块。
函数是对其本机实现的精简包装,这可能因平台而异。使用
将类型映射到相应的本机类型
#define mythread pthread_t
和类似的。
为了避免每次需要获取或释放锁时都必须使用条件语句使代码混乱,即使在单线程构建中也可以调用大多数函数,这使得它们成为无操作。因此,只有在产生或加入线程时,或用于替代它们时才需要条件。 (尝试在单线程构建上调用线程创建函数将产生编译器错误。)
这也意味着即使在不支持线程的情况下,抽象的线程类型也需要映射到某些东西,因为可能存在这些类型的局部变量(同样,以避免条件语句使代码混乱)。到目前为止,我将它们映射到单线程构建中的 void
,如下所示:
#ifdef HAVE_POSIX_THREADS
#define mythread pthread_t
#else
#ifdef HAVE_FUNKY_THREADS
#define mythread FThread
#else /* no supported native thread API available, single-threaded build */
#define mythread void
#endif
然而,这是以无法使用如下声明为代价的:
mythread new_thread;
(这将声明一个 void
变量,在 C 中无效)。这可以通过仅使用指向这些类型的指针来解决——事实上,每个初始化任何这些类型的函数都会分配内存和 returns 指向它的指针。
问题:我想知道对于这样的情况是否有更优雅的方法
做就好了
#else
#define mythread int
#endif
并且您可能可以制作所有精简包装函数 static inline xxx xxx(xxx)
,尽管这并不是真正必要的,因为几乎每个现代编译器都只是将 inline
视为提示。完成这些之后,几乎每个现代编译器都会内联您的包装函数并找出 mythread new_thread
中的 new_thread
实际上未被使用,然后对其进行优化。
注意:(纯个人评论,可能跑题了)除非必要,否则不要使用宏条件。死代码和未使用变量消除等编译器优化是您的好朋友。虽然不是你的情况,但在 GNU 编码指南中鼓励使用
if(some_compiler_flag){ //some_compiler_flag is an 0/1 flag
xxx;
}
而不是
#ifdef some_compiler_flag
xxx
#endif
现代编译器会编译出相同的结果,而前者可读性更强,对静态分析工具和自动完成更友好,让您的生活更轻松。
对于您的情况,如果可以重构您的代码,我更愿意将所有这些线程类型用作 不透明指针。例如,
//In public headers
union ptr_my_thread_;
typedef ptr_my_thread union ptr_my_thread_;
int my_thread_init(ptr_my_thread);
...
然后在您的实现文件中处理特定于平台的代码
//In config.h, which takes care of platform dependent flags
#define HAVE_POSIX_THREADS 1
#define HAVE_FUNKY_THREADS 0
...
//In your implementation file
#include <config.h>
union ptr_my_thread_{
pthread_t *posix;
FThread *funky;
}
int my_thread_init(ptr_my_thread new_thread){
if(HAVE_POSIX_THREADS){
xxx;
}
}
这将加强平台相关代码的模块化和分离。
我正在为C 项目中的线程相关函数编写一个抽象层。并非所有可以为其构建代码的平台都支持多线程;这些平台有替代品。
如果支持多线程,头文件会定义一个预处理器宏,因此预处理器条件可用于启用和禁用仅在多线程(或不在多线程时)才需要的代码块。
函数是对其本机实现的精简包装,这可能因平台而异。使用
将类型映射到相应的本机类型#define mythread pthread_t
和类似的。
为了避免每次需要获取或释放锁时都必须使用条件语句使代码混乱,即使在单线程构建中也可以调用大多数函数,这使得它们成为无操作。因此,只有在产生或加入线程时,或用于替代它们时才需要条件。 (尝试在单线程构建上调用线程创建函数将产生编译器错误。)
这也意味着即使在不支持线程的情况下,抽象的线程类型也需要映射到某些东西,因为可能存在这些类型的局部变量(同样,以避免条件语句使代码混乱)。到目前为止,我将它们映射到单线程构建中的 void
,如下所示:
#ifdef HAVE_POSIX_THREADS
#define mythread pthread_t
#else
#ifdef HAVE_FUNKY_THREADS
#define mythread FThread
#else /* no supported native thread API available, single-threaded build */
#define mythread void
#endif
然而,这是以无法使用如下声明为代价的:
mythread new_thread;
(这将声明一个 void
变量,在 C 中无效)。这可以通过仅使用指向这些类型的指针来解决——事实上,每个初始化任何这些类型的函数都会分配内存和 returns 指向它的指针。
问题:我想知道对于这样的情况是否有更优雅的方法
做就好了
#else
#define mythread int
#endif
并且您可能可以制作所有精简包装函数 static inline xxx xxx(xxx)
,尽管这并不是真正必要的,因为几乎每个现代编译器都只是将 inline
视为提示。完成这些之后,几乎每个现代编译器都会内联您的包装函数并找出 mythread new_thread
中的 new_thread
实际上未被使用,然后对其进行优化。
注意:(纯个人评论,可能跑题了)除非必要,否则不要使用宏条件。死代码和未使用变量消除等编译器优化是您的好朋友。虽然不是你的情况,但在 GNU 编码指南中鼓励使用
if(some_compiler_flag){ //some_compiler_flag is an 0/1 flag
xxx;
}
而不是
#ifdef some_compiler_flag
xxx
#endif
现代编译器会编译出相同的结果,而前者可读性更强,对静态分析工具和自动完成更友好,让您的生活更轻松。
对于您的情况,如果可以重构您的代码,我更愿意将所有这些线程类型用作 不透明指针。例如,
//In public headers
union ptr_my_thread_;
typedef ptr_my_thread union ptr_my_thread_;
int my_thread_init(ptr_my_thread);
...
然后在您的实现文件中处理特定于平台的代码
//In config.h, which takes care of platform dependent flags
#define HAVE_POSIX_THREADS 1
#define HAVE_FUNKY_THREADS 0
...
//In your implementation file
#include <config.h>
union ptr_my_thread_{
pthread_t *posix;
FThread *funky;
}
int my_thread_init(ptr_my_thread new_thread){
if(HAVE_POSIX_THREADS){
xxx;
}
}
这将加强平台相关代码的模块化和分离。