Какова хорошая практика копирования векторных данных в кортеж с другим типом данных элемента?

#c #vector #copy #tuples

#c #вектор #Копировать #Кортежи

Вопрос:

Я использую GCC 4.8.3 с C 11.

У меня есть a std::vector<uint8_t> , и мне нужно скопировать его содержимое в a std::tuple . Элементы кортежа описывают структуру данных вектора. Пример.: std::tuple<uint32_t,uint16_t> будет соответствовать вектору из 6 байт, где первые четыре байта принадлежат uint32_t , а вторые два байта uint16_t .

Это требование не подлежит изменению, поскольку оно является частью более крупного шаблонного класса, но разбито для упрощения моего вопроса.

Редактировать: конечность гарантируется правильной. Спасибо WhozCraig

Сейчас у меня есть два варианта

 template<typename T, int TupleIndex, unsigned int BufferPosition>
void extractBufferToTuple(Tamp;amp; tuple, std::vector<uint8_t>amp;buffer) {
    std::get<TupleIndex>(tuple) = *(typename std::tuple_element<TupleIndex,T>::type*)amp;buffer[BufferPosition];
}
 

и

 template<typename T, int TupleIndex, unsigned int BufferPosition>
void extractBufferToTuple(Tamp;amp; tuple, std::vector<uint8_t>amp;buffer) {
    std::copy(amp;buffer[BufferPosition], amp;buffer[BufferPosition]   sizeof(typename std::tuple_element<TupleIndex,T>::type), (uint8_t*)amp;std::get<TupleIndex>(tuple));
}
 

Вызов этого будет выглядеть примерно так

 std::tuple<uint32_t,uint32_t> myTuple;
std::vector<uint8_t> buffer;
buffer.resize(6);
uint32_t value0 = 123;
uint16_t value1 = 456;
std::copy((uint8_t*)amp;value0,(uint8_t*)amp;value0 sizeof(value0),amp;buffer[0]);
std::copy((uint8_t*)amp;value1,(uint8_t*)amp;value1 sizeof(value1),amp;buffer[sizeof(value0)]);


extractBufferToTuple<decltype(myTuple),0,0>(std::forward<decltype(testClass)::Tuple>(myTuple),buffer);        
extractBufferToTuple<decltype(myTuple),1,sizeof(std::tuple_element<0,decltype(myTuple)>::type)>(std::forward<decltype(myTuple)>(myTuple),buffer);    
 

Является ли один из них допустимым и безопасным подходом или есть какая-то лучшая практика без каких-либо возможных ошибок?

Комментарии:

1. Игнорирование очевидной разбивки, когда исходное и целевое конечное представление несовместимы?

2. Спасибо, что указали на это. Это также гарантировано.

3. Примечание к подписи: ссылка на значение rvalue для параметра out кажется сомнительной. Если кто-то передает временный, то вывод не может быть использован. Это только усложняет реализацию почти без очевидной выгоды. Кроме того, источники, доступные только для чтения, должны передаваться по ссылке на const . (Передача неконстантной ссылки lvalue сообщает вызывающим: «Я могу изменить этот объект».)

4. Это очень хорошие подсказки. Не думал об этом, но имеет смысл.

Ответ №1:

Возможно, что-то в этом роде:

 template <typename T>
size_t extractBuffer(const std::vector<uint8_t>amp; buffer, size_t start_index, T* to) {
  std::memcpy(to, amp;buffer[start_index], sizeof(*to));
  return start_index   sizeof(*to);
}

template <typename ... Ts>
size_t extractBuffer(const std::vector<uint8_t>amp; buffer, size_t start_index,
                     std::tuple<Ts...>* to) {
  auto extractHelper = [amp;](autoamp; ... elems) {
    auto _ = {(start_index = extractBuffer(buffer, start_index, amp;elems)) ...};
    return start_index;
  };
  return std::apply(extractHelper, *to);
}
 

ДЕМОНСТРАЦИЯ

Комментарии:

1. Я думаю, мы могли бы переписать std::apply, чтобы я мог использовать его в C 11?

2. std::apply может быть заменен чуть более подробным подходом , основанным на std::index_sequence . Последнее является функцией C 14, но тривиально использовать свою собственную в C 11, намного проще, чем std::apply . Вы должны быть в состоянии найти его.

3. Большое вам спасибо. Я постараюсь реализовать это на C 11. Но ваш подход выглядит очень красиво.

4. Была ли какая-либо мысль об использовании memcpy вместо copy в данном конкретном случае? Просто интересно

5. static_cast<void>(_); также может быть полезно для автоматического предупреждения о неиспользуемой переменной.

Ответ №2:

Было немного сложно заставить его работать, но это то, что я придумал для C 11. Это не так гладко, как Игорь

 template<size_t TupleIndex>
struct ExtractBufferToTuple {
    template<typename T, typename Iterator>
    static inline void extractBufferToTuple(Tamp; tuple, Iterator bufferPosition) {
        auto nextBufferPosition = bufferPosition - sizeof (typename ::std::tuple_element<TupleIndex, T>::type);
        std::copy(nextBufferPosition, bufferPosition, (uint8_t*) amp; ::std::get<TupleIndex>(tuple));
        ExtractBufferToTuple < TupleIndex - 1 > ::extractBufferToTuple(tuple, nextBufferPosition);
    }
};

template<>
struct ExtractBufferToTuple<0> {
    template<typename T, typename Iterator>
    static inline void extractBufferToTuple(Tamp; tuple, Iterator bufferPosition) {
        auto nextBufferPosition = bufferPosition - sizeof (typename ::std::tuple_element<0, T>::type);
        std::copy(nextBufferPosition, bufferPosition, (uint8_t*) amp; ::std::get<0>(tuple));
    }
};

template<typename T, typename ... TArgs>
void extractBufferToTuple(Tamp; tuple,const std::vector<uint8_t>amp;buffer) {
    size_t tupleByteSize = byteSize < TArgs...>();
    if (buffer.size() == tupleByteSize) {
        ExtractBufferToTuple<::std::tuple_size<typename ::std::decay<T>::type>::value - 1 > ::extractBufferToTuple(tuple, buffer.end());
    }
}