C 样式数组与 std::库接口的数组

C-style array vs std::array for library interface

本文关键字:数组 接口 std 样式      更新时间:2023-10-16

我想编写一个带有提供读取函数的接口的库。 C 样式数组容易出错,但允许传递任何大小的缓冲区。 C++数组更安全,但强制要求以一定大小构造。

// interface.h
// C-style array
int read (std::uint8_t* buf, size_t len);
// C++ array
int read (std::array<std::uint8_t, 16>& buff)

我怎样才能两全其美?

我在考虑函数模板,但对于库接口来说似乎并不实用。

template <size_t N>
int read (std::array<std::uint8_t, N>& buf);

编辑std::vector可能是一个很好的候选者,但如果我们认为char*std::array没有动态分配。

编辑我非常喜欢gsl::span的解决方案。我被困在 C++14 所以没有std::span.我不知道使用第三个库 (gsl) 是否会成为问题/允许。

编辑我不认为使用char而不是另一种类型会对答案产生一些影响,所以更清楚的是操纵字节。我把char改成std::uint8_t

编辑由于 C++11 保证退货std::vector将移动而不是复制,因此退货std::vector<std::uint8_t>是可以接受的。

std::vector<std::uint8_t> read();

您可以执行标准库的操作:使用一对迭代器。

template <typename Iter> int read(Iter begin, Iter end)
{
// Some static assets to make sure `Iter` is actually a proper iterator type
}

它为您提供了两全其美的优势:安全性略高,并且能够读取缓冲区的任意部分。此外,它还允许您读取非应急容器。

我怎样才能拥有两个世界中最好的?

通过使用std::vector

  • 就像std::arrays一样:它比C阵列更安全。
  • 像 C 数组一样:它允许您使用必须能够接受任意大小数组的函数。

编辑std::vector并不一定意味着动态分配(如动态存储持续时间)。这取决于所使用的分配器。您仍然可以提供用户指定的堆栈分配器。

我会违背原则,说对于read类型的函数,void*指针和大小可能是最好的选择。这是世界各地任何无格式读取函数所采用的方法。

为什么不使用gsl::span,它的目的是消除一系列连续对象的指针和长度参数对?这样的东西会起作用:

int read(gsl::span<uint8_t> buf)
{
for (auto& elem : buf)
{
// Do whatever with elem
}
}

唯一的问题是,不幸的是,gsl::span不是C++标准的一部分(也许它可能在 C++20 中),安装它需要一个像 GSL-lite 这样的库

。以下是更多关于span的细节,来自赫伯·萨特。

你真的关心底层容器的类型吗?

template<typename Iterator>
int read_n(Iterator begin, size_t len);

假设此函数返回读取的元素数,我也会将返回类型更改为size_t

char *dyn = new char[20];
char stat[20];
std::vector<char> vec(20);
read(dyn, 20);
read(stat 20);
read(vec.begin(), 20);

我认为在设计lib的界面时,您需要考虑使用它的位置。

带有带有"char *"的 C 接口的库可用于多种语言(C、C++ 等)。使用 std::array 会限制库的潜能客户端。

第三个可能的变体:

struct buf_f allocBuf();
int rmBuf( struct buf_t b );
int read( struct buf_f b );
char * bufData( struct buf_f b );
size_t bufSize( struct buf_f b );

当然,它可以以更优雅的方式用C++重写。

您可以使用 make 包装器函数,该函数是委托给 C 接口函数的模板函数:

int read(std::uint8_t* buf, size_t len);
template <size_t N>
int read(std::array<std::uint8_t, N>& buf)
{
return read(buf.data(), buf.size());
}

当我需要通过 C ABI 进行某些操作时,我发现这样的结构很有用,但不想失去C++提供的一些舒适感,因为模板函数是作为库客户端代码的一部分编译的,不需要与 C ABI 兼容,而模板函数调用的函数与 C ABI 兼容

只需返回一个std::vector<uint8_t>,除非这是一个 DLL,在这种情况下,请使用 C 样式接口。

注意:问题从char更改为uint8_t后,答案从std::string更改为std::vector