Могу ли я иметь шаблонную функцию с переменным значением, возвращающую разные типы?

Я делаю некоторую десериализацию из байтового массива и сделал шаблон Variaad ExtractData, чтобы он работал как

QByteArray data; // (this works just like std::vector<char>)
std::vector<std::any> values = ExtractData<float, char>(data); // read a float, char sequentially from data
float readFloat = std::any_cast<float>(values[0]);
float readChar = std::any_cast<char>(values[1]);

но все же, много шаблонов для расшифровки вещей.
В идеале я бы хотел что-то вроде

float readFloat;
char readChar;
std::tie(readFloat, readChar) = ExtractData<float, char>(data);

Структура ExtractData в основном

using anyVec = std::vector<std::any>;

// one type resolution
template<typename T>
anyVec ExtractData(const QByteArray& data, anyVec out = {}){
    // extract T value, assign to std::any, push_back into out
    return outVec;
}

// multiple types resolution
template<typename T, typename... Rest>
typename std::enable_if<(sizeof...(Rest) > 0), anyVec>::type
ExtractData(const QByteArray& data, anyVec out = {}){
    // extract T value, assign to std::any, push_back into out
    return ExtractData<Rest...>(data, out);
}

Я просто не понимаю, как я мог сделать ExtractData<type1, type2, ...>(data) вернуть std::tuple<type1, type2, ...> поскольку вся эта исходная информация о типе теряется, когда список типов шаблонов "раскручивается". Это вообще возможно? Извините, если я упускаю что-то очевидное, я все еще довольно плохо знаком с C++11 и новее.

Я вижу, что мог бы создать шаблон для фиксированного количества типов, но, к сожалению, это не мой вариант использования.


Я использую Qt 5.11, C++14 (опущено experimental в пространствах имен здесь), но рад слышать советы C++17 тоже. Точный фрагмент кода, который я использую: https://gist.github.com/tjakubo2/dc3e6897bf42f3bed78933031e53786b

2 ответа

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

template <typename T>
T ExtractOnePiece(const QByteArray& data, int& offset);

template <typename... Ts>
std::tuple<Ts...> ExtractData(const QByteArray& data) {
    int offset = 0;
    return {ExtractOnePiece<Ts>(data, offset)...};
}

Демо Специализации ExtractOnePiece для каждого типа, который вы хотите поддержать, оставлено в качестве упражнения для читателя.

Спасибо, Бен и Сеф из #include Сервер Discord, как предложил Игорь в ответе здесь, я приземлился с:

template<typename T>
std::tuple<T> ExtractSingle(const QByteArray& data, size_t offset){
    // pull T_val from data at given offset
    return std::tuple<T>{T_val};
}

template<typename T>
std::tuple<T> ExtractData(const QByteArray& data, size_t offset = 0){
    return ExtractSingle<T>(data, offset);
}

template<typename T, typename... Rest>
typename std::enable_if<(sizeof...(Rest) > 0), std::tuple<T, Rest...>>::type
ExtractData(const QByteArray& data, size_t offset = 0){
    auto val = ExtractSingle<T>(data, offset);
    return std::tuple_cat(std::move(val), ExtractData<Rest...>(data, offset + sizeof(T)));
}

который делает именно то, что я хотел. Я думаю, что это можно сделать немного более производительным, но это не требуется для моего приложения на данный момент. Ура!

Другие вопросы по тегам