【问题标题】:Dealing with complex, nested types in API在 API 中处理复杂的嵌套类型
【发布时间】:2020-02-20 19:49:34
【问题描述】:

所以我们假设有一个与图像处理相关的项目。我们有一个算法可以计算两个图像之间的“分数”(不管它是什么),所以:

double score(Image i1, Image i2)

如果我们有多个帧(图像),我们要为所有帧(图像匹配)计算它:

std::vector<std::pair<std::pair<int, int>, double>> score(std::vector<Image> images); // [1]

int 对表示图像向量中的图像索引,因为我们将所有匹配到所有

不幸的是,我们从许多设备中获取图像,其中每个设备都可以有多个流(然后每个流中有多个连续的帧),这使得这个级别变得复杂:

std::vector<std::vector<std::vector<std::pair<std::pair<int, int>, double>>>>
     ^devices    ^streams    ^frames     ^score   ^image indices

您将如何处理必须返回如此复杂类型的函数?

只是将它包装在 typedef 中并正确命名?或者,也许您只会让用户使用更简单的 API 版本,即使用一个向量 [1] 并进一步自行包装?或者这种情况有什么花哨的模式吗?

【问题讨论】:

  • 你错过了[1]中的第二个std::pair吗?
  • Typedefs(即using X = Y;)应该是保持其可读性的第一步。但这只是在设计本身合理的情况下,这很难判断(您不想将每个级别的集合包装在某种类中以提供其他有用的东西吗?)...
  • 如果是API,我真的看不出device/stream/frames参数的原因...score()不应该关心它们。而对于score(std::vector&lt;Image&gt; images),我个人会返回一个二维数组(自定义类或vector&lt;vector&lt;double&gt;&gt;
  • 是的,谢谢@Evg

标签: c++ types architecture api-design naming


【解决方案1】:

在不了解数据的情况下,一种选择是创建结构或类来包装各个部分:

namespace your_company {
struct index {
    std::pair<int, int> indexes;
};
struct frame {
    std::pair<index, double> scores;
};
struct stream {
    std::vector<frame> frames;
};
struct device {
    std::vector<stream> streams;
};
}

如果需要,您甚至可以向结构添加更多信息。如:

struct device {
    std::vector<stream> streams;
    std::string name;
};

那么你的函数将返回一个device。用户代码可能如下所示:

your_company::device d = your_function();
d.streams[0].frames[0].scores[0].pair[0].....

【讨论】:

  • 确实,它可以比很多嵌套类型更干净,但我怕为了这个目的引入很多新结构。
【解决方案2】:

一般来说,您通过给事物起正确的名称来处理复杂性。

当我在[1] 中看到您的 API 时,我看到了一个图形,其中图像是图形的节点,您将边作为邻接列表返回。邻接信息也可以以对称邻接矩阵的形式存储。为您的问题使用适当的术语可能使您可以访问大量工具,例如像 Boost.Graph 这样的库。它可能是您编写自己的可重用组件的起点。您甚至可能会发现人们处理了您试图解决的问题,但使用更抽象的术语。

这只是一种选择,当您考虑正确命名事物时,就会发生奇妙的事情。

关于复杂性的第二次跳跃,我认为可能由调用者决定他们想要输入算法的块的大小,因为这似乎人为地限制了函数的用例.如果你认为情况并非如此,你还是应该给事物起正确的名字。您可以为此使用结构和类,这可能是一个很差的选择:

using AdjacencyList = std::vector<std::pair<std::pair<int, int>, double>;
// Info that might also be represented as a matrix or symmetric matrix.

class SimilarityData
{
    public:
        const AdjacencyList &
        getAdjacencyList(
            int device,
            int stream,
            int frame ) const;
};

【讨论】:

  • 我同意,这似乎限制了函数的用例。我会考虑要么只提供不太复杂的功能(例如,只使用一对),要么两者都重载。
  • @Mateusz 太棒了!只需考虑更复杂的重载可能属于以特定方式使用您的函数的调用代码的可能性。鉴于您在该级别有很多额外的上下文,应该更容易在那里找出正确的名称。如果确实有多个独立的调用者以一种特定的方式使用您的函数,那么想出正确的名称也应该变得容易得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多