如何编写C++库以使用span的任何实现<T>?

How to write C++ library to work with any implementation of span<T>?

本文关键字:lt 实现 gt 任何 C++ 何编写 span      更新时间:2023-10-16

我正在编写一个I/O库,用户需要在其中提供要读取或写入的内存块。 让我的图书馆接受span<T>似乎是最自然的选择,因为:

  • 它不会将容器选择强加给用户。 他们可以使用原始指针、std::vector或任何其他具有连续存储的容器。
  • 它允许我确保内存访问是安全的,因为我知道缓冲区的大小。

不幸的是,在Boost,GSL和标准库中存在span<T>的竞争实现(截至C++20)。 这些实现的界面是兼容的,从用户的角度来看,它们使用哪一个应该无关紧要。

如何对 I/O 函数进行编码,以便它们与span的任何各种实现一起工作?

我目前能想到的唯一方法是包括我自己的span实现,它可以从任何具有::element_type.data().size()的东西隐式构造。

重要的是,仍然支持来自容器的隐式转换,以便用户可以简单地传递std::vector。 例如:

void read_data(span<float> data);
std::vector<float> foo(1024);
read_data(foo);

您可能有一个配置步骤供用户构建库(或者只是一个仅用于标头库的 config.h):

像这样:

配置.h:

// include guard omitted.
#if defined(SPAN_TYPE) // To allow custom span
template <typename T> using lib_span = SPAN_TYPE<T>;
#elif defined(USE_STD_SPAN)
template <typename T> using lib_span = ::std::span<T>;
#elif defined(USE_BOOST_SPAN)
template <typename T> using lib_span = ::boost::span<T>;
// ...
#else
# error "No span provided"
#endif

然后在代码中使用lib_span<T>

编辑 3:

我会把我的旧帖子留给任何感兴趣的人。但是,我刚刚找到了明显的解决方案...

#include <user_span>
//dive into your namespace to avoid name collisions
namespace YOUR_LIB
{
//supply custom type
template <class T, SIZET size = DEFAULT>
using span = ::user_span<T, size>;
}
//actually include the library
#include <your_lib>

使用这种方法,您可以在任何地方使用span并像往常一样编程。

源语言:

一种解决方案是使用具有默认值的跨度模板。喜欢:

template <template<class,size_t> class Span>
void read_data(Span<float> data);

但是,使用不同签名作为容器的各种库可能会遇到问题。因此,更好的方法是执行以下操作:

template <class Span>
void read_data(Span data);

您现在缺少值的类型,但您应该能够通过以下方式检索它:

using T = typename Span::value_type

此外,您可能希望向函数添加一些std::enable_if(或概念),以检查Span是否实际提供您正在使用的成员函数。

实际上,上述所有内容都可能导致代码非常嘈杂。如果只有一个简单的案例,更简单的方法可能是使用库用户的using声明来定义 span。

上述方法可能无法很好地处理std::vector重载,因为类型签名有些相似。您可以通过提供显式std::vector重载来解决此问题,这些重载执行正确的强制转换并调用Span版本。

编辑:

从您的评论中,我收集到您要将未指定的类型(任何容器)转换为未指定的类型(任何范围)。这既没有意义,也是不可能的。您需要在某处定义其中一种类型。

话虽如此,您可以编写独立于转换的代码,并让用户提供实际的转换。喜欢:

template <class Span>
void read_data_impl(Span data);
template <class Container>
void read_data(Container data)
{
read_data_impl(convert(data));
}

然后,用户需要提供:

template <class Container>
auto convert(Container& data)
{
return USERSPAN(data);
}

编辑2:

我的代码中有一个错误。转换函数中的data必须通过引用传递。此外,如果手动传递允许传递容器值的T,可能会有所帮助。因此,您最终会得到:

template <class Span>
void read_data_impl(Span data);
template <class Container>
void read_data(Container data)
{
read_data_impl(convert(data));
}
template <class T, class Container>
void read_data(Container data)
{
read_data_impl(convert<T>(data));
}

对于转换:

template <class T, class Container>
auto convert(Container& data)
{
return convert_impl<T>(data);
}
template <class Container>
auto convert(Container& data)
{
return convert_impl<typename Container::value_type>(data);
}

然后,用户必须提供以下功能:

template <class T, class Container>
auto convert_impl(Container& data)
{
return USERSPAN<T>(data);
}