C++ 共享库中使用的数据类型 API
data types used in C++ shared library API
我正在编写一个 C++ 共享库 (.so
),其主要功能是处理由 OpenCV 处理的图像的特征。然而,这个共享库中的算法并不是专门针对视觉的——它可以接收来自雷达、激光雷达等的测量结果。
在构建库时,我试图将 OpenCV 依赖项排除在外,而是使用更通用的 Eigen3 矩阵库。
我的问题是关于应用程序代码和我的库之间的接口。我有一个 public 方法,它将接受来自应用程序的每个特征的位置和速度测量列表:
void add_measurements(std::vector< std::tuple<double, double> > pos,
std::vector< std::tuple<double, double> > vel);
是否最好让 API 接口上的数据结构尽可能原始,就像上面那样?或者我应该强制应用程序代码提供 std::vector<Eigen::Vector2d>
用于测量?
此外,我是否应该允许应用程序代码中的 std::vector<cv::Point2f>
然后在内部转换为 Eigen3 或其他内容?这个似乎是最没用的,因为该库仍将依赖于 OpenCV。
您可以在不牺牲性能的情况下使用泛型来桥接不同的数据约定。
缺点是界面的学习曲线可能更高。
首先,您可以接受 iterators 而不是接受 vectors,它允许用户在其他容器中提供数据,例如 [=34] =]数组和列表.
template<typename AccessType, typename PosIter, typename VelIter>
void add_measurements(PosIter p1, PosIter p2, VelIter v1, VelIter v2)
{
// instantiate type to access coordinates
AccessType access;
// process elements
// Internal representation
std::vector<std::pair<double, double>> positions;
for(; p1 != p2; ++p1)
positions.emplace_back(access.x(*p1), access.y(*p1));
std::vector<std::pair<double, double>> velocities;
for(; v1 != v2; ++v1)
positions.emplace_back(access.x(*v1), access.y(*v1));
// do stuff with the data
}
然后,如果他们有一个奇怪的数据类型,他们想像这样使用:
struct WeirdPositionType
{
double ra;
double dec;
};
他们可以创建一个类型来访问它的内部点坐标:
// class that knows how to access the
// internal "x/y" style data
struct WeirdPositionTypeAccessor
{
double x(WeirdPositionType const& ct) const { return ct.ra; }
double y(WeirdPositionType const& ct) const { return ct.dec; }
};
然后得到 'plugged in' 到泛型函数:
int main()
{
// User's weird and wonderful data format
std::vector<WeirdPositionType> ps = {{1.0, 2.2}, {3.2, 4.7}};
std::vector<WeirdPositionType> vs = {{0.2, 0.2}, {9.1, 3.2}};
// Plugin the correct Access type to pull the data out of your weirt type
add_measurements<WeirdPositionTypeAccessor>(ps.begin(), ps.end(), vs.begin(), vs.end());
// ... etc
}
当然可以提供现成的 Access
类型的常用点库如OpenCv
:
struct OpenCvPointAccess
{
double x(cv::Point2d const& p) const { return p.x; }
double y(cv::Point2d const& p) const { return p.y; }
};
那么使用可以简单的使用那个:
add_measurements<OpenCvPointAccess>(ps.begin(), ps.end(), vs.begin(), vs.end());
请记住,您可以重载您的函数以支持多种容器。如果您认为两者都有用,则无需在它们之间进行选择。
所以主要考虑的是每种方式的开销,以及是否要添加对 Eigen 的依赖。如果库的未来版本会有不同的实现,你不想使用泄漏抽象。
另一个有用的技巧是添加类型别名,例如,在命名空间内:
using point2d = std::tuple<double, double>;
您稍后可以更改为:
using point2d = Eigen::vector2d;
或者:
using point2d = cv::Point2f;
您可以通过将它们包装在结构中来使它们更加不透明。如果这样做,未来的更改将破坏与以前 ABI 的兼容性,但不会破坏 API.
我正在编写一个 C++ 共享库 (.so
),其主要功能是处理由 OpenCV 处理的图像的特征。然而,这个共享库中的算法并不是专门针对视觉的——它可以接收来自雷达、激光雷达等的测量结果。
在构建库时,我试图将 OpenCV 依赖项排除在外,而是使用更通用的 Eigen3 矩阵库。
我的问题是关于应用程序代码和我的库之间的接口。我有一个 public 方法,它将接受来自应用程序的每个特征的位置和速度测量列表:
void add_measurements(std::vector< std::tuple<double, double> > pos,
std::vector< std::tuple<double, double> > vel);
是否最好让 API 接口上的数据结构尽可能原始,就像上面那样?或者我应该强制应用程序代码提供 std::vector<Eigen::Vector2d>
用于测量?
此外,我是否应该允许应用程序代码中的 std::vector<cv::Point2f>
然后在内部转换为 Eigen3 或其他内容?这个似乎是最没用的,因为该库仍将依赖于 OpenCV。
您可以在不牺牲性能的情况下使用泛型来桥接不同的数据约定。
缺点是界面的学习曲线可能更高。
首先,您可以接受 iterators 而不是接受 vectors,它允许用户在其他容器中提供数据,例如 [=34] =]数组和列表.
template<typename AccessType, typename PosIter, typename VelIter>
void add_measurements(PosIter p1, PosIter p2, VelIter v1, VelIter v2)
{
// instantiate type to access coordinates
AccessType access;
// process elements
// Internal representation
std::vector<std::pair<double, double>> positions;
for(; p1 != p2; ++p1)
positions.emplace_back(access.x(*p1), access.y(*p1));
std::vector<std::pair<double, double>> velocities;
for(; v1 != v2; ++v1)
positions.emplace_back(access.x(*v1), access.y(*v1));
// do stuff with the data
}
然后,如果他们有一个奇怪的数据类型,他们想像这样使用:
struct WeirdPositionType
{
double ra;
double dec;
};
他们可以创建一个类型来访问它的内部点坐标:
// class that knows how to access the
// internal "x/y" style data
struct WeirdPositionTypeAccessor
{
double x(WeirdPositionType const& ct) const { return ct.ra; }
double y(WeirdPositionType const& ct) const { return ct.dec; }
};
然后得到 'plugged in' 到泛型函数:
int main()
{
// User's weird and wonderful data format
std::vector<WeirdPositionType> ps = {{1.0, 2.2}, {3.2, 4.7}};
std::vector<WeirdPositionType> vs = {{0.2, 0.2}, {9.1, 3.2}};
// Plugin the correct Access type to pull the data out of your weirt type
add_measurements<WeirdPositionTypeAccessor>(ps.begin(), ps.end(), vs.begin(), vs.end());
// ... etc
}
当然可以提供现成的 Access
类型的常用点库如OpenCv
:
struct OpenCvPointAccess
{
double x(cv::Point2d const& p) const { return p.x; }
double y(cv::Point2d const& p) const { return p.y; }
};
那么使用可以简单的使用那个:
add_measurements<OpenCvPointAccess>(ps.begin(), ps.end(), vs.begin(), vs.end());
请记住,您可以重载您的函数以支持多种容器。如果您认为两者都有用,则无需在它们之间进行选择。
所以主要考虑的是每种方式的开销,以及是否要添加对 Eigen 的依赖。如果库的未来版本会有不同的实现,你不想使用泄漏抽象。
另一个有用的技巧是添加类型别名,例如,在命名空间内:
using point2d = std::tuple<double, double>;
您稍后可以更改为:
using point2d = Eigen::vector2d;
或者:
using point2d = cv::Point2f;
您可以通过将它们包装在结构中来使它们更加不透明。如果这样做,未来的更改将破坏与以前 ABI 的兼容性,但不会破坏 API.