【问题标题】:Cross-Platform API Development for Windows, Linux, et al适用于 Windows、Linux 等的跨平台 API 开发
【发布时间】:2017-12-27 04:32:59
【问题描述】:

我的任务是为我们的硬件传感器构建一个跨平台 API,以使它们对 3rd 方开发人员更有用。目前,我们有很多遗留应用程序,其中硬件层构建在 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 打开文件,而不是CreateFile 在 Windows 中?从跨平台的角度来看,这是否对我有帮助,或者这只是一种愚蠢的方式,限制了我在重叠 io 等方面的选择?我可能已经回答了我自己的问题...

感谢任何可以提供任何建议、指南或最佳做法的人。

【问题讨论】:

  • 我不明白你为什么要继承Device。使用条件编译,无论如何您将只有特定于平台的实现,而调用者并不关心它是在 Windows 中还是在 linux 中。在处理硬件的时候,我个人的感觉是:只用C,可选地提供C++ wrappers。但这实际上取决于您在实施中究竟需要做什么。
  • 感谢您的评论。最终,这就是重点——调用者不需要关心,他们所拥有的只是一个头文件和相应的库文件。他们永远不需要担心幕后发生的事情。您对inherited 方法的看法是正确的,但从我的角度来看,它感觉更易于维护——但也许情况并非如此。我更倾向于 C 而不是 C++
  • A C API 可能更容易从其他语言中使用。不过,您是使用 C 包装器在 C++ 中实现它还是以其他方式实现它取决于您。
  • 我同意@melak47,C API 是要走的路。除非您要为 API 提供源代码并让用户为他们的目标操作系统重新编译它,否则即使在同一个操作系统上编译器之间的差异(命名约定 ABI 等)也会让生活变得困难。

标签: c++ c linux api window


【解决方案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

【讨论】:

  • 感谢您的回复。我喜欢这个实现,并且可以看到它运行良好。再次感谢
【解决方案2】:

作为经常处理这个问题的另一面的人(使用 3rd 方库与硬件系统交互),我建议您远离 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 定义明确、简洁且易于使用。

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

【讨论】:

  • 感谢您的全面回复。我几乎已经在脑海中确定了我将编写一个严格的 C API 并将整个库保留在 C 中——我没有看到使用 C++ 来实现我的巨大好处。就像你说的那样,我可以在一个头文件中定义完整的 API,只需要最少的额外包含(可能是几个标准的,比如 stdint.h 等)另外感谢你的链接 - 他们给了我很好的洞察力关于如何构建我的 API。
猜你喜欢
  • 2010-11-17
  • 1970-01-01
  • 2011-10-22
  • 1970-01-01
  • 2015-04-09
  • 2013-06-23
  • 1970-01-01
  • 1970-01-01
  • 2010-11-29
相关资源
最近更新 更多