适用于Windows,Linux等的跨平台API开发

时间:2017-07-21 08:05:15

标签: c++ c linux api window

我已经完成了为我们的硬件传感器构建跨平台API的任务,以使第三方开发人员更容易使用它们。目前,我们有许多遗留应用程序,其中硬件层是在GUI内部和周围构建的。

我想要做的是删除硬件层并实现一个可靠但设计良好的跨平台API,然后可以将其提供给第三方开发人员,以帮助将我们的硬件集成到他们的平台中。

我意识到在Stack Overflow术语中,这可能是一个红旗问题,但无论如何我都会问它。

我们现有的代码库是多语言(C#,C ++ / Qt,Delphi等),但只能在Windows上运行。我想进一步推进这项工作,至少包括Linux,可能还有一些Mac / OSX支持。

从更大的角度来看,我对未来任务的理解是创造一个"黑盒子"说到API。诸如connect之类的函数将接受一些参数并传递某种设备handle,其中底层连接代码对用户是隐藏的。然后可以将此句柄传递到各种control函数中,以使设备执行特定功能 - callbacks可用于异步通知。

现在这引出了我的问题:

使用connectreadwrite等函数需要了解操作系统的底层IO子系统,是否可以编写代码库的这一部分作为C ++中的类而不是扁平的C?

因此,请考虑原始device的以下伪代码:

class Device : abstract {
    bool connect(...) abstract
}

#ifdef _WIN32

class WindowsDevice : public Device {
    // Calls Windows-specific functions
    bool connect(...)
}

#elif __linux

class LinuxDevice : public Device {
    // Calls Linux-specific functions
    bool connect(...)
}

#else
#error Bad platform
#endif

然后是API函数......

Device *inst;

bool DEVICE_Connect(...) {
    // bind the device instance here
    #ifdef _WIN32
        inst = new WindowsDevice();
    #else
        inst = new LinuxDevice();
    #endif
}

bool DEVICE_Command(uint8_t cmd) {
    if (!inst)
        FAIL;

    inst->SendCommand(cmd);
}

或者,如果实现是更加C样式的API,例如以下伪代码:

#ifdef _WIN32
HANDLE *inst;
#elif __linux
void *inst;
#else
#error Bad platform
#endif


bool DEVICE_Connect(void *handle) {
    #ifdef _WIN32
        // Windows connect-to-whatever function
    #elif __linux
        // Linux connect-to-whatever function
    #else
        return FALSE;
    #endif

}

bool DEVICE_Command(uint8_t cmd) {
    if (!inst)
        FAIL;

    #ifdef _WIN32
        // Windows write command
    #elif __linux
        // Linux write command
    #else
        return FALSE;
    #endif
}

基本上,我所希望的是那些曾经处于这种情况的人的一些good-practice建议。使用C ++实现代码是否有任何优点,然后为最终用户公开C包装器?这一切都应该用扁平的C来完成吗?

最后一个问题:

有问题的硬件设备有各种连接选项,从TCP和UDP套接字到USB HID,然后是简单的旧RS232,因此我需要调用的基础功能将是多种多样的。

考虑到这一点,使用最低级别的函数调用是否有任何实际意义(或好处) - 我的意思是,有意使用fopen来打开文件,而不是Windows中的CreateFile?从跨平台的角度来看,这对我有帮助吗?这只是一种愚蠢的方式,限制我在重叠io等方面的选择?我可能回答了我自己的问题......

感谢任何能提供任何建议,指导或最佳实践的人。

2 个答案:

答案 0 :(得分:3)

作为定期处理此问题另一方的人(使用第三方库与硬件系统连接),我建议您远离C ++以获取API。虽然它似乎可以提供重要的功能集,但对于最终用户来说C API更加简单。您甚至可以在C ++中实现后端并将其包装到您建议的C API中。如果您愿意,这种方法还允许您相对容易地为其他语言(如Python)创建包装器。

通常实现此问题的方法是在简单的C头中严格指定API,该头具有最小的包含依赖性,最佳无。此标头将指定库的完整功能,包括用户可能需要使用的所有函数,返回码,状态和数据结构。

然后,后端将为每个操作系统实现此行为。理想情况下,此平台相关代码将根据您的需要编译为静态或共享库,最终用户只需链接到它。该库不一定需要是开源的(大多数情况下,如果您是供应商,它们不是)。您可以分发标头API和共享对象(.so),DLL,存档(.a)或静态库(.lib),或者要求用户编译整个后端。

在我看来,我所使用的API的一个例子就是......

它不是开源后端,但仍然通过C样式API为最终用户提供了一整套功能。我非常怀疑后端是用C实现的,但我不确定。

与我之前所说的相反还有跨平台的C ++ API。想到的是...

它有一大组C ++标头,用户可以访问它们来控制硬件。它尝试使用用户可以访问,拥有和控制的大量类来建模完整的功能集。

如果我在C ++中这样做,我可能会考虑在API中使用PIMPL惯用法来保护最终用户免于在硬件上造成太多问题。

总的来说,最重要的设计实践是使API定义明确,简洁易用。

如果难以记录或提供使用示例,则在实践中使用可能会很复杂。

答案 1 :(得分:1)

您可能希望将您的实现划分为独立于平台且依赖于平台的代码,即

bool DEVICE_Command(uint8_t cmd) {
    if (!inst)
        FAIL;
   if (cmd ==1 )
      platform_cmd_1();
   else
      platform_cmd_default(cmd);

}

platform_cmd和platform_cmd_default将在不同的文件夹中实现,并仅针对目标平台进行编译,即。你的文件夹结构可能是

src\
  device.c
  platform_device.h
api\
  deviceapi.h
platform\
  win32\
    platform_device.c
  linux
    platform_device.c
  macos
   platform_device.c