【问题标题】:How to write C++ library to work with any implementation of span<T>?如何编写 C++ 库以使用 span<T> 的任何实现?
【发布时间】:2019-11-01 15:02:35
【问题描述】:

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

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

不幸的是,在 Boost、GSL 和标准库(从 C++20 开始)中存在 span&lt;T&gt; 的竞争实现。这些实现的接口是兼容的,从用户的角度来看,他们使用哪一个并不重要。

如何编码我的 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”来选择使用哪个是可能的。
  • 要么有一个构建系统或预处理器标志来选择使用哪一个,要么让你的所有函数接受span模板并接受任何span-like。
  • @MilesBudnek:template 的问题是不再有转换为span 以获得相同/预期的跨度接口;我的意思是template &lt;typename Span&gt; void read_data(Span span) { span.begin(); /*..*/ } 不会接受float data[42]void read_data(std::span&lt;float&gt; span) { span.begin(); /*..*/ } 会。并强制用户显式调用read_data(std::span&lt;float&gt;(data) 是可悲的。
  • @Jarod42 使用config.h 意味着在库构建时做出选择。这将阻止库的不同用户(在同一系统上)使用他们首选的跨度实现。
  • @marack 不是它将所有不同的spans 调整为一个内部使用的library::span

标签: c++ std-span


【解决方案1】:

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

类似:

config.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&lt;T&gt;

【讨论】:

  • 哦!虽然我虽然关于我的帖子,但你基本上有同样的想法:D
【解决方案2】:

编辑 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:

我的代码中有一个错误。 convert 函数中的data 必须通过引用传递。此外,如果您通过 allow 手动为容器值传递 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);
}

【讨论】:

  • 不幸的是,这种方法不允许从它们的容器类型隐式转换为span。用户需要显式构造一个span - 这可能不如传统的原始指针和大小参数那么明显。
  • 对于这个用例,您将提供做正确事情的重载。对吗?
  • 为容器提供显式重载会限制用户使用特定的容器集。 span 传递数据的意义在于,它让用户可以安全地传递数据,而库不需要知道实际的容器是什么。它可能是一个自定义容器,只存在于库永远不知道的用户代码中。
  • convert(data) 可能适用于类类型(感谢 ADL),它不适用于内置类型 c-array:Demo
  • @Jarod42 首先,他没有说 c-arrays 是预期的。其次,在我的方法中,这是用户的责任,最后你必须在调用之前声明转换函数,或者你必须把它放在首位。 Demo 另外,感谢您找到我的错误! convert函数需要通过引用来捕获...
猜你喜欢
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 2018-01-05
  • 1970-01-01
  • 2018-07-16
相关资源
最近更新 更多