如何在不乱库的情况下实现条件编译API?

How to implement conditional compilation without messing up the library API?

我有一个库可以使用 OpenCL 框架进行 GPU 计算。遗憾的是,OpenCL 并非在所有平台上都可用。但是我仍然希望能够在这些平台上编译我的代码,只是排除 OpenCL 功能。

我认为这个问题适用于所有你想有条件地编译一些可能并不总是可用的外部资源的情况,它会扰乱你的库 API。

目前我是这样设置的:

CMake:

if(ENABLE_OPENCL)
    add_definitions(-DENABLE_OPEN_CL)
    find_package(OpenCL REQUIRED)
    include_directories(${OpenCL_INCLUDE_DIR})
    target_link_libraries(mylibrary ${OpenCL_LIBRARY})
endif()

C++

// settings.hpp, exposed to public API
class settings
{
    int general_setting_1;
    bool general_setting_2;
    // ... Other general settings
#ifdef ENABLE_OPEN_CL
    int open_cl_platform_id;
    // ... Other settings available only when OpenCL is available
#endif
    // More settings, possibly also conditionally compiled on other external libraries
};
// computation.cpp, internal to the library

#ifdef ENABLE_OPEN_CL
#include <CL/cl.hpp>
#endif

void do_things()
{
    // ... 

#ifdef ENABLE_OPEN_CL
    if(settings.open_cl_platform_id != -1)
    {
        // Call OpenCL code
    }
#endif

    // ...
}

所以当我编译库时,如果我想启用 OpenCL,我会 cmake .. -DENABLE_OPEN_CL

这行得通,但是如果客户端正在使用用 ENABLE_OPEN_CL 编译的库,它会强制客户端定义相同的 ENABLE_OPEN_CL,否则包含的库的头文件与那个不匹配在客户端使用,发生了非常糟糕的事情。

这打开了一大堆蠕虫,例如,如果客户忘记这样做怎么办?如果它对其他东西使用相同的标识符名称怎么办?

我可以避免这种情况吗?如果不是,有什么方法可以验证头文件在客户端和库上是否匹配,并导致编译错误?或者至少抛出 运行 次异常?这种情况的正确方法是什么?

最明显的方法是将 open_cl_platform_id 保留为 settings 的成员,即使不支持 OpenCL。如果用户在尚未为其编译库时尝试使用 OpenCL 功能,则用户会收到 运行 次错误。

或者,有两个头文件 settings_no_open_cl.hppsettings_open_cl.hpp,并要求用户包含正确的头文件。