Как написать библиотеку C ++ для работы с любой реализацией span <T>? - PullRequest
0 голосов
/ 19 июня 2019

Я пишу библиотеку ввода / вывода, в которой пользователю необходимо предоставить блоки памяти для чтения или записи.Принятие моей библиотекой span<T> кажется наиболее естественным соответствием, поскольку:

  • Это не навязывает пользователю выбор контейнера.Они могут использовать необработанные указатели, std::vector или любой другой контейнер с непрерывным хранилищем.
  • Это позволяет мне обеспечить безопасный доступ к памяти, поскольку я знаю размер буфера.

К сожалению, есть конкурирующие реализации span<T> в Boost, GSL и стандартной библиотеке (начиная с C ++ 20).Интерфейс этих реализаций совместим, и с точки зрения пользователя не должно иметь значения, какую из них они используют.

Как я могу кодировать свои функции ввода / вывода, чтобы они работали с любой из различных реализаций span ??1021 *

Важно, чтобы неявные преобразования из контейнеров все еще поддерживались, чтобы пользователь мог просто передать std::vector.Например:

void read_data(span<float> data);

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

Ответы [ 2 ]

1 голос
/ 20 июня 2019

У вас может быть шаг конфигурации для пользователя, чтобы построить вашу библиотеку (или просто 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<T> в своем коде.

0 голосов
/ 19 июня 2019

РЕДАКТИРОВАТЬ 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 везде и программировать как обычно.

ORIGINAL:

Одним из решений было бы использование шаблона для диапазона со значением по умолчанию.Например:

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 предоставляет функции-члены, которые вы используете.

На практике все вышеперечисленное может привести к оченьшумный код.Если у вас есть только простой случай, более простым способом может быть определение span с объявлением using пользователя библиотеки.

Выше, вероятно, не будет хорошо работать с перегрузками std::vector, потому что сигнатуры типовнесколько похожи.Вы можете решить эту проблему, предоставив явные перегрузки std::vector, которые выполняют правильное приведение и вызывают версию Span.

EDIT:

Из вашего комментария я понимаю, чтоВы хотите преобразовать неопределенный тип (любой контейнер) в неопределенный тип (любой диапазон).Это не имеет смысла и не возможно.Вам нужно будет где-то определить один из типов.

При этом вы можете написать независимый от преобразования код и позволить пользователю обеспечить фактическое преобразование.Например:

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 в функции преобразования должен быть передан по ссылке.Кроме того, вероятно, будет полезно, если вы передадите 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);
}
...