我应该如何替换 API 中的 vector<uint8_t>::const_iterator?

How should I replace vector<uint8_t>::const_iterator in an API?

我接到了完善编解码器库界面的任务。我们正在使用 C++17,我只能使用标准库(即没有 Boost)。目前,有一个 Decoder class 大致如下所示:

class Decoder : public Codec {

public:

    struct Result {
        vector<uint8_t>::const_iterator new_buffer_begin;
        optional<Metadata>              metadata;
        optional<Packet>                packet;
    };

    Result decode(vector<uint8_t>::const_iterator buffer_begin,
                  vector<uint8_t>::const_iterator buffer_end);

private:
    // irrelevant details
};

调用者实例化一个Decoder,然后通过

向解码器提供数据流
  1. 从文件中读取一大块数据(但将来可能有其他来源),并将其附加到 vector<uint8_t>.

  2. 调用 decode 函数,传递向量的迭代器。

  3. 如果返回的 Resultnew_buffer_begin 与传递给 decodebuffer_begin 相同,则表示没有缓冲区中的数据足以解码任何内容,调用者应返回步骤 1。否则,调用者使用已解码的 MetadataPacket 对象,并返回步骤 2,使用 new_buffer_begin 为下一关。

我不喜欢这个界面并需要帮助改进的地方:

C++20 将具有 std::span,它可以满足您的需求:

    Result decode(std::span<uint8_t const> buffer);

std::span<T> 在语义上等同于 T* buffer, size_t size.


在 C++17 中,有一些 span 类型的实现等同于 std::span,例如 GSL's gsl::span. See .

如果您不能使用任何外部库,请考虑编写您自己的 span 类型,否则 uint8_t const* buffer_begin, uint8_t const* buffer_end 可以工作。

我同意强制要求 vector 是不合适的,并赞赏您为使界面更有用所做的努力。

如果 decode 需要连续的 uint8_t 序列,久经考验(也是最灵活)的解决方案就是采用 const uint8_t*std::size_t (或者两个指针,但指针和长度更惯用)。

从 C++20 开始,您可以使用 std::span<const uint8_t>. Or going back to pointers, if you really want to use modern library tools for the sake of it, you can confuse people with std::experimental::observer_ptr.

类型的一个参数来完成此操作

您也可以考虑制作 decode 一个接受任何迭代器对的模板,并且(如果需要连续性)要求迭代器反映 连续序列。但是把所有东西都变成模板并不总是你想要的,而且它并不总是有用的。

除了@Justin 对 的有效建议:

  • 您可能还想考虑使用 std::byte 而不是 uint8_t,因此:
    Result decode(std::span<const std::byte> buffer);
    
    或者,如果您使用的是 C++17,请使用 C++ Guidelines Support library 中的 span 实现:
    #include <gsl/span>
    // etc.
    Result decode(gsl::span<const std::byte> buffer);
    
  • 如果要支持从原始内存以外的容器解码,请使用任意迭代器(在 C++17 及更早版本中)或可能使用范围(在 C++20 中)。迭代器版本:

    template <typename InputIt>
    Result decode(InputIt start, InputIt end) { /* etc. */ }
    
  • Decoder 继承自 Codec 而不是相反,这很可疑。

  • 回调是否是一个好的选择这个问题(对我来说)在没有看到代码的情况下很难回答。但是确实要使用 std::variant 来表达您拥有数据包或元数据的事实;如果您使用变体 std::visit.
  • 而不是回调,您也可以 "combine" 替代方案