spl*_*cer 17 c++ idiomatic binary-data c++17
我被赋予了完善编解码器库接口的任务。我们使用的是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
};
Run Code Online (Sandbox Code Playgroud)
调用者实例化一个Decoder,然后通过以下方式将数据流馈送到解码器:
从文件中读取大量数据(但将来可能还会有其他来源),并将其附加到vector<uint8_t>。
调用该decode函数,并为其向量传递迭代器。
如果返回Result的new_buffer_begin与buffer_begin传递给的相同,则decode意味着缓冲区中没有足够的数据来解码任何内容,并且调用方应返回到步骤1。否则,调用方将使用已解码的Metadata或Packet对象。 ,然后返回到步骤2,new_buffer_begin用于下一遍。
我对这个介面不满意,需要改善的地方:
使用vector<uint8_t>::const_iterator似乎过于具体。有没有更通用的方法不强制调用者使用vector?我当时正在考虑仅使用C风格的界面;一个uint8_t *和一个长度。有没有相当通用的C ++替代品?
如果有足够的数据来解码某些内容,则只有metadata 或 packet将有一个值。我认为std::variant2个回调(每种类型一个)将使此代码更具自说明性。我不确定哪个更惯用。每种方法的优缺点是什么,是否有更好的方法?
Lig*_*ica 18
我同意强制性操作vector是不合适的,并称赞您为使界面更有用而进行的尝试。
如果decode期望使用的连续序列uint8_t,则经过测试(最灵活)的解决方案只是采用a const uint8_t*和a std::size_t(或两个指针,但是指针和长度更惯用)。
在C ++ 20中,您可以使用type一个参数来执行此操作std::span<const uint8_t>。或者回到指针,如果您真的想为此使用现代的库工具,则可以使人们感到困惑std::experimental::observer_ptr。
您也可以考虑制作decode一个模板,该模板接受任何迭代器对,并且(如果需要连续性的话)强制执行该模板(即使仅凭文档即可),以使这些迭代器反映一个连续的序列。但是,将所有内容制作为模板并不总是您想要的,也不总是有用的。
ein*_*ica 15
除了@Justin的有效跨度建议之外:
std::byte而不是uint8_t,因此:
Result decode(std::span<const std::byte> buffer);
Run Code Online (Sandbox Code Playgroud)
或者,如果您使用的是C ++ 17,请使用C ++ Guidelines Support库中的span实现:
#include <gsl/span>
// etc.
Result decode(gsl::span<const std::byte> buffer);
Run Code Online (Sandbox Code Playgroud)如果要支持从原始内存以外的容器进行解码,请使用任意迭代器(在C ++ 17和更早版本中)或可能的范围(在C ++ 20中)。迭代器版本:
template <typename InputIt>
Result decode(InputIt start, InputIt end) { /* etc. */ }
Run Code Online (Sandbox Code Playgroud)Decoder从a 继承Codec而不是从其他途径继承,这很可疑。
std::variant来表达您拥有数据包或元数据的事实;如果您使用variant'代替回调,您也可以“组合”替代方案std::visit。C ++ 20将具有std::span您想要的功能:
Result decode(std::span<uint8_t const> buffer);
Run Code Online (Sandbox Code Playgroud)
std::span<T>在语义上等效于T* buffer, size_t size。
在C ++ 17中,有一些与span类型等效的实现std::span,例如GSL的实现 gsl::span。请参阅什么是“跨度”,何时应使用?。
如果您不能使用任何外部库,请考虑编写自己的span类型,否则
uint8_t const* buffer_begin, uint8_t const* buffer_end可以使用。