在某些配置中不需要C类型的良好做法

时间:2019-03-22 01:27:31

标签: c types c-preprocessor

我正在为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中无效)。可以通过仅使用指向这些类型的指针来解决此问题-实际上,初始化这些类型中的任何一个的每个函数都会分配内存并返回指向该内存的指针。

问题:我想知道在这种情况下是否有更优雅的方法。

1 个答案:

答案 0 :(得分:1)

做就可以了

#else
#define mythread int
#endif

您可能可以使所有精简包装函数成为static inline xxx xxx(xxx),但这并不是真正必要的,因为几乎每个现代编译器都只是将inline视为提示。完成这些操作后,几乎每个现代编译器都会内联您的包装函数,并找出new_thread中的mythread 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;
    }
}

这将强制实现模块化和平台相关代码的分离。