我想编写一个带有提供读取功能的接口的库。 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();
答案 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阵列更安全。 编辑: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
。