Reputation: 2066
I'm writing an I/O library where the user needs to supply blocks of memory to be read from or written to. Having my library accept a span<T>
seems the most natural fit since:
std::vector
, or any other container with contiguous storage.Unfortunately there are competing implementations of span<T>
in Boost, GSL and the standard library (as of C++20). The interface of these implementations are compatible, and from the user perspective it should not matter which one they use.
How can I code my I/O functions so that they work with any of the various implementations of span
?
The only approach I can think of at the moment is to include my own implementation of span
which would be implicitly constructable from anything with a ::element_type
, .data()
and .size()
.
It is important that implicit conversions from containers still be supported so that the user can simply pass a std::vector
. For example:
void read_data(span<float> data);
std::vector<float> foo(1024);
read_data(foo);
Upvotes: 0
Views: 478
Reputation: 860
EDIT 3:
I will leave my old posts for anyone who is interested. However, I just found the obvious solution...
#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>
With this approach you can just use span everywhere and program as usual.
ORIGINAL:
One solution would be to use a template for the span with a default value. Like:
template <template<class,size_t> class Span>
void read_data(Span<float> data);
However, you can run into problems with the various libraries using different signature for their containers. Thus, the better way would be to do:
template <class Span>
void read_data(Span data);
You are missing the type of the values now, but you should be able to retrieve it with:
using T = typename Span::value_type
In addition, you might want to add some std::enable_if
(or Concepts) to your functions, which checks if Span
actually provides the member functions you are using.
In practice, all of the above can result in very noisy code. If you only have a simple case, the easier way might be to define span with a using
declaration of the library user.
The above will probably not work well with the std::vector
overloads because the type signatures are somewhat similar. You can solve this by providing explicit std::vector
overloads which do the right casts and call the Span
version.
EDIT:
From your comment I gather that you want to convert a unspecified type (any container) to a unspecified type (any span). This neither makes sense nor is possible. You will need to define one of the types somewhere.
That being said, you could write conversion independent code and let the user provide the actual conversion. Like:
template <class Span>
void read_data_impl(Span data);
template <class Container>
void read_data(Container data)
{
read_data_impl(convert(data));
}
The user would then need to supply:
template <class Container>
auto convert(Container& data)
{
return USERSPAN(data);
}
EDIT 2:
There was a bug in my code. The data
in the convert function has to be passed by reference. In addition, it is probably helpful if you pass allow passing a T
for the container value by hand. Thus you end up with:
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));
}
And for conversion:
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);
}
The user then has to supply the following function:
template <class T, class Container>
auto convert_impl(Container& data)
{
return USERSPAN<T>(data);
}
Upvotes: 0
Reputation: 217583
You might have a config step for user to build your library (or just a config.h for header library only):
Something like:
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
And then use lib_span<T>
in your code.
Upvotes: 1