我应该如何替换 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
,然后通过
向解码器提供数据流
从文件中读取一大块数据(但将来可能有其他来源),并将其附加到 vector<uint8_t>
.
调用 decode
函数,传递向量的迭代器。
如果返回的 Result
的 new_buffer_begin
与传递给 decode
的 buffer_begin
相同,则表示没有缓冲区中的数据足以解码任何内容,调用者应返回步骤 1。否则,调用者使用已解码的 Metadata
或 Packet
对象,并返回步骤 2,使用 new_buffer_begin
为下一关。
我不喜欢这个界面并需要帮助改进的地方:
使用 vector<uint8_t>::const_iterator
似乎过于具体。是否有更通用的方法不强制调用者使用 vector
?我正在考虑只使用 C 风格的接口;一个 uint8_t *
和一个长度。是否有相当通用的 C++ 替代方案?
如果有足够的数据来解码某些东西,只有 metadata
或 packet
会有值。我认为 std::variant
或 2 个回调(每种类型一个)会使这段代码更加自我记录。我不确定哪个更惯用。各自的优缺点是什么,还有更好的方法吗?
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" 替代方案
我接到了完善编解码器库界面的任务。我们正在使用 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
,然后通过
从文件中读取一大块数据(但将来可能有其他来源),并将其附加到
vector<uint8_t>
.调用
decode
函数,传递向量的迭代器。如果返回的
Result
的new_buffer_begin
与传递给decode
的buffer_begin
相同,则表示没有缓冲区中的数据足以解码任何内容,调用者应返回步骤 1。否则,调用者使用已解码的Metadata
或Packet
对象,并返回步骤 2,使用new_buffer_begin
为下一关。
我不喜欢这个界面并需要帮助改进的地方:
使用
vector<uint8_t>::const_iterator
似乎过于具体。是否有更通用的方法不强制调用者使用vector
?我正在考虑只使用 C 风格的接口;一个uint8_t *
和一个长度。是否有相当通用的 C++ 替代方案?如果有足够的数据来解码某些东西,只有
metadata
或packet
会有值。我认为std::variant
或 2 个回调(每种类型一个)会使这段代码更加自我记录。我不确定哪个更惯用。各自的优缺点是什么,还有更好的方法吗?
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
,因此:
或者,如果您使用的是 C++17,请使用 C++ Guidelines Support library 中的 span 实现:Result decode(std::span<const std::byte> buffer);
#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" 替代方案