【问题标题】:Generic API publishing techniques通用 API 发布技术
【发布时间】:2014-03-06 19:43:09
【问题描述】:

我正在为内部项目设计下一代框架架构。本质上,有一个运行时系统为可以动态加载/卸载的应用程序提供 API。

框架中有一个自定义“设备”库。这些设备的当前接口非常通用;想想 posix load/unload/read/write/etc... 该框架的重要原则之一是应用程序需要对设备细节知之甚少。

不幸的是,这个接口已经分解到应用程序开发人员最终重写应用程序本身的常用功能的地方。

我正在寻找有关设计这样的框架 API 的建议,甚至是开始阅读的地方。框架中的“设备”作者可以用来发布界面的一组好的工具是什么? 发布界面的最佳方式是什么?

【问题讨论】:

  • 您能否更具体地说明当前系统中缺少什么,促使应用程序开发人员放弃它?您需要添加什么样的 API 函数,如果它们已经与加载/卸载/读取/写入函数一起添加,会导致什么问题?
  • 当前系统过于通用。我可能会创建一个进行信号处理的设备和另一个是电机控制器的设备。我可以用来发布的唯一接口方法是加载/卸载/读取/写入等...我希望设备作者能够为该设备发布他们自己的自定义方法/功能,但允许应用程序不一定需要#include 任何额外的东西。他们应该能够查询已安装设备的框架,然后获取设备作者发布的接口。
  • 所以,假设应用程序不包含任何额外的内容,并且可以发现并可能调用额外的函数,它将如何决定是否/何时调用它们,以及如何处理结果?未更改的 App 是否会从某个地方(例如配置文件、键盘或 UI 小部件)读取指定调用这些函数的输入,还是会编辑 App 以指定调用?
  • 应用程序开发人员将知道他正在尝试与什么设备通信。他将在纸质文档 (ICD) 中了解如何与设备对话。因此,该应用程序将具有取决于设备知识的代码。唯一重要的解耦是运行时可链接的应用程序模块需要某些特定的调用,而这些调用在链接时无法解决。特定的设备 API 应由框架本身管理和提供。

标签: c++ api user-interface interface


【解决方案1】:

好的 - 一个提案 - 可能与您想要的有很大不同,但反馈会帮助您找到有用的东西。

对于同步“调用”,您希望您的应用模块发送所需驱动程序功能的指示以及无论多少参数,然后检索一些结果。这可以通过具有第二个不同的读/写流以通用方式实现,在该流上传输函数和值的编码。因此,假设驱动程序 API 包括:

string get_stuff(string x, int y, double d[]);

这不是代码 - 它是框架/应用可以打印/解析并可能用于验证数据是否已相应发送和使用的文本。

然后应用模块将“调用”作为函数标识符和输入流写入驱动程序,然后读取结果(假设应用模块具有保存所需参数值的同名变量)。

driver_api << "get_stuff " << escape(x) << ' ' << y << " [" << d.size() << "] ";
for (auto i = d.begin(); i != d.end(); ++i)
    driver_api << ' ' << *i;
driver_api << '\n';

std::getline(driver_api, result);

创建一个自定义流包装器需要更多的工作(半小时?),它可以包装 driver_api 并在上面插入空格或其他分隔符/分隔符,支持容器流式传输,和/或以二进制形式,让您编写更清晰和/或更快的面向价值的上述版本,例如:

(driver_api << "get_stuff" << x << y << d) >> result;

您还可以编写普通的 C++ 函数来包装上面的内容以供应用程序模块调用:

string get_stuff(const std::string& x, int y, const std::vector<double>& d)
{
    string result;
    (driver_api_ << "get_stuff" << x << y << d) >> result;
    return result;
}

在驱动程序方面,您将编写匹配的反序列化例程以从流中恢复应用模块指定的值。

对于特定的体系结构,您可以获取库以允许更方便地调用函数,利用调试信息或 ABI 知识等,而以上只需要标准 C++ 并且可以编写成可移植的(如果这样做,请注意一点字节序值的二进制序列化)。

EDIT - 二进制序列化示例(现在只是输出/运行但输出没有仔细检查有效性/大多数东西是固定宽度的,但字符串是 NUL 终止的,并且以大小为前缀的容器/不发送后续字段类型信息,但是很容易创建一个版本):

#include <iostream>
#include <vector>

#include <winsock2.h>
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int32_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned uint32_t;

class Binary_IOStream
{
  public:
    Binary_IOStream(std::istream& i, std::ostream& s) : i_(i), s_(s) { }
    typedef Binary_IOStream This;
    This& operator<<(int8_t x) { return write(x); }
    This& operator<<(int16_t x) { return write(htons(x)); }
    This& operator<<(int32_t x) { return write(htonl(x)); }
    This& operator<<(uint8_t x) { return write(x); }
    This& operator<<(uint16_t x) { return write(htons(x)); }
    This& operator<<(uint32_t x) { return write(htonl(x)); }
    This& operator<<(const std::string& s)
        { s_.write(s.c_str(), s.size() + 1); // include NUL, but could size-prefix
          return *this; }
    This& operator<<(double x) { return write(x); }

    template <typename T>
    This& operator<<(const std::vector<T>& v)
        { return write_range(v.begin(), v.end()); }

    template <typename Iterator>
    This& write_range(Iterator begin, Iterator end)
    {
        operator<<(std::distance(begin, end));
        while (begin != end)
            operator<<(*begin++);
        return *this;
    }

  private:
    template <typename T>
    This& write(const T& t)
        { s_.write((const char*)&t, sizeof t); return *this; }

    std::istream& i_;
    std::ostream& s_;
};

int main()
{
    Binary_IOStream bs(std::cin, std::cout);

    bs << ('A' << 24) + ('b' << 16) + ('c' << 8) + 'D';
    bs << "hello world!";
    std::vector<double> v;
    v.push_back(3.14);
    v.push_back(2.72);
    bs << v;
}

【讨论】:

  • 这正是我的想法。您能否建议将呼叫编码为流的任何标准方法?快速的东西(我不想要一堆 xml 包装器)最好使用原生格式的原始数据,但我可能能够获取 c/c++ 库的标准。
  • 我不知道,但我会编辑一些代码来说明如何完成。
  • 我想我完全理解你的提议。我现在可以编写类似的代码,没问题。如果有一种标准的方法来做到这一点,那就太好了。我几乎不敢相信没有。我认为这是我搜索的下一步。我知道 JSON 会做这样的事情,但 JSON 不够快(它编码 ascii 字符串数据)格式。
  • @Lother:进行了足够多的编辑,希望能为您开启新的篇章。如果某个地方没有图书馆,我会感到惊讶......我过去为公司写过同样的东西,但不是 OSS。一定有人有。或者,在每个变量二进制序列化的更高级别上,有像 Google protobuf 这样的解决方案......可能会感兴趣。
  • 托尼,谢谢。这是一个聪明的实现。如果我没有找到这个库(我很快就会看看 protobuf)我肯定会使用你提供的。与您进行这次对话就非常有见地。再次感谢!
猜你喜欢
  • 1970-01-01
  • 2013-01-28
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 1970-01-01
  • 2015-02-22
相关资源
最近更新 更多