我正在为内部项目设计下一代框架架构。基本上有一个运行时系统,它为应用程序提供API,可以动态加载/卸载。
在框架中存在一个自定义“设备”库这些设备的当前接口非常通用;认为posix加载/卸载/读/写/等...框架的一个重要原则是应用程序需要对设备细节知之甚少。
不幸的是,这个界面已经分解为应用程序开发人员最终重写应用程序本身的常用功能。
我正在寻找建议甚至是开始阅读有关设计这样的框架API的地方。框架中的“设备”作者可以使用什么样的好工具来发布接口? 发布界面的最佳方式是什么?
答案 0 :(得分:2)
好的 - 一个建议 - 可能与你想要的大不相同,但反馈将有助于回归有用的东西。
对于同步“调用”,您希望应用程序模块发送所需驱动程序功能的指示以及多个参数,然后检索一些结果。这可以通过具有第二个不同的读/写流以通用方式实现,在该第二个不同的读/写流上传输函数和值的编码。所以,说驱动程序API包括:
string get_stuff(string x, int y, double d[]);
这不是代码 - 框架/应用程序可以打印/解析的文本,可能用于验证数据是否相应地发送和使用。
app模块然后将“call”作为函数标识符和输入的流写入驱动程序,然后读取结果(假设app模块具有保存所需参数值的同名变量)。
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 ++函数来包装上面的app模块来调用:
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 ++并且可以编写为可移植的(如果做的话可以小心字节值的二进制序列化。)
编辑 - 二进制序列化的例子(现在只输出/运行但输出没有仔细检查有效性/大多数固定宽度但字符串是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;
}