我被赋予了完善编解码器库接口的任务。我们使用的是C ++ 17,我只能使用标准库(即没有Boost)。当前,有一个Decoder
类,看起来像这样:
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
,然后通过以下方式将数据流提供给解码器
从文件中读取大量数据(但将来可能还会有其他来源),并将其附加到vector<uint8_t>
。
调用decode
函数,并为其向量传递迭代器。
如果返回的Result
的{{1}}与传递给new_buffer_begin
的{{1}}相同,则意味着缓冲区以解码任何内容,调用者应返回到步骤1。否则,调用者将使用已解码的buffer_begin
或decode
对象,并使用Metadata
返回到步骤2。下一次通过。
我对此界面不满意,需要改进:
使用Packet
似乎过于具体。有没有更通用的方法不强制调用者使用new_buffer_begin
?我当时正在考虑仅使用C风格的界面; vector<uint8_t>::const_iterator
和长度。有没有相当通用的C ++替代方法?
如果有足够的数据来解码内容,则只有vector
或 uint8_t *
会有值。我认为metadata
或2个回调(每种类型一个)将使此代码更具自说明性。我不确定哪个更惯用。每种都有什么利弊,还有更好的方法吗?
答案 0 :(得分:18)
我同意强制vector
是不合适的,并为您使界面更有用的努力表示赞赏。
如果decode
期望uint8_t
的连续序列,那么经过实践检验(最灵活)的解决方案就是采用const uint8_t*
和std::size_t
(或另外两个指针,但指针和长度更常见。
在C ++ 20中,您可以使用一个std::span<const uint8_t>
类型的参数来执行此操作。还是回到指针,如果您真的想为此使用现代的图书馆工具,可以将人们与std::experimental::observer_ptr
混淆。
您也可以考虑将decode
用作接受任何迭代器对的模板,并且(如果需要连续性的话)强制执行命令(即使仅凭文档即可),以使迭代器反映连续序列。但是,将所有内容制作成模板并不总是您想要的,也不总是有用。
答案 1 :(得分:16)
除了@Justin的spans的有效建议:
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
而不是回调,也可以“组合”替代方案。答案 2 :(得分:5)
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
。参见What is a "span" and when should I use one?。
如果您不能使用任何外部库,请考虑编写自己的span
类型,否则
uint8_t const* buffer_begin, uint8_t const* buffer_end
可以工作。