用于库接口的C风格数组vs std :: array

时间:2018-02-16 15:14:25

标签: c++ arrays

我想编写一个带有提供读取功能的接口的库。 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();

8 个答案:

答案 0 :(得分:6)

您可以执行标准库的操作:使用一对iterators

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

它为您提供了两全其美的优势:更好的安全性和读入缓冲区任意部分的能力。它还允许您阅读非传统容器。

答案 1 :(得分:2)

  

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

使用std::vector

  • std::array一样:它比C阵列更安全。
  • 与C数组类似:它允许您使用必须能够获取任意大小数组的函数。

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

答案 2 :(得分:1)

我会反对这一点,并说对于read - 类型函数取void*指针和大小可能是最好的选择。这是全世界任何未格式化的读取函数采用的方法。

答案 3 :(得分:1)

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

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

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

Here是有关span的详细信息,来自Herb Sutter。

答案 4 :(得分:0)

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

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);

答案 5 :(得分:0)

我认为在设计lib的界面时,你需要考虑使用它的考虑因素。

带有“char *”的C接口的库可以与各种语言(C,C ++和其他语言)一起使用。使用std :: array限制了lib的潜在客户端。

第三种可能的变体:

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 ++重写它。

答案 6 :(得分:0)

您可以使用make wrapper函数,该函数是委托给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兼容

答案 7 :(得分:-3)

只需返回std::vector<uint8_t>,除非这是一个DLL,在这种情况下使用C风格的界面。

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