С++ возвращает данные из варианта в соответствии с заданным именем типа

У меня есть следующий шаблон функции для возврата определенного типа данных из VARIANT в соответствии с заданным typename.

template <typename T>
T VariantGetValue(VARIANT Variant) {

    std::string S(typeid(T).name());

    if (S == "wchar_t* __ptr64") { return Variant.bstrVal; }
    if (S == "unsigned int") { return Variant.uintVal; }
}

Итак, поскольку мне нужно было вернуть тип unsigned int из VARIANT, я попытался использовать вышеуказанную функцию, например:

return VariantGetValue<unsigned int>(CV);

Но, к сожалению, компилятор здесь игнорирует регистр if (S == "....) и выдает ошибку:

C2440 — «возврат»: невозможно преобразовать из «BSTR» в «целое число без знака»

Но если я удалю строку if (S == "wchar_t* __ptr64") { return Variant.bstrVal; }, компилятор выдаст мне только следующее предупреждение:

C4715 — «VariantGetValue»: не все пути управления возвращают значение

Могу ли я подавить эту ошибку и продолжить? Это безопасно или есть альтернативные способы сделать это без ошибок компилятора?


person GTAVLover    schedule 20.07.2017    source источник
comment
Что это за тип VARIANT?   -  person Justin    schedule 20.07.2017
comment
@Justin VARIANT type — это структура, которая может содержать несколько типов данных.   -  person GTAVLover    schedule 20.07.2017
comment
Это своего рода MS WinAPI структура. Стандартный C++ не распознает объекты VARIANT и BSTR. Это определения типов MS.   -  person Ron    schedule 20.07.2017
comment
Итак, кажется, что нет решения для такого рода вещей?   -  person GTAVLover    schedule 20.07.2017
comment
Как насчет использования std::variant и не игнорировать предупреждения?   -  person Passer By    schedule 20.07.2017
comment
@PasserBy Я снова забыл std::variant, я также нашел его здесь и попробую теперь, к сожалению, я все еще не могу использовать boost::variant из-за этой проблемы, которую до сих пор нельзя исправить.   -  person GTAVLover    schedule 20.07.2017
comment
Типы шаблонов должны быть разрешены во время компиляции, этот дизайн никогда не будет работать. Вместо этого вы можете использовать специализацию шаблона   -  person M.M    schedule 20.07.2017
comment
@M.M Вот что я потерял :-)   -  person GTAVLover    schedule 20.07.2017


Ответы (1)


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

template < typename T >
T VariantGetValue(VARIANT) = delete;

template <>
unsigned int VariantGetValue<unsigned int>(VARIANT Variant)
{
    VARIANT var;
    InitVariantFromUInt32(unsigned int{}, &var);

    if (Variant.vt != var.vt)
        throw std::runtime_error("bad get");
    return Variant.uintVal;
}

template <>
BSTR VariantGetValue<BSTR>(VARIANT Variant)
{
    if (/* check that Variant stores wchar_t* __ptr64 */)
        throw std::runtime_error("bad get");
    return Variant.bstrVal;
}

Это, кстати, то, что std::get делает для std::variant.

#include <iostream>
#include <variant>

using Variant = std::variant<int,std::string>;

int main()
{
    Variant v(13);

    std::cout << std::get<int>(v) << '\n'; // 13
  //std::cout << std::get<std::string>(v) << '\n'; // std::bad_variant_access
}

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

#include <iostream>
#include <stdlib.h>
#include <string.h>

// Implement a mock VARIANT, don't take this code too seriously

typedef unsigned int VARTYPE;
typedef char* BSTR;
enum { VT_UI4, VT_BSTR };

struct VARIANT
{
    VARIANT() : bstrVal(nullptr) {}
    VARTYPE vt;
    union {
        unsigned int uintVal;
        BSTR bstrVal;
    };
};

void InitVariantFromUInt32(unsigned int u, VARIANT * v)
{
    v->vt = VT_UI4;
    v->uintVal = u;
}

void InitVariantFromString(char const * s, VARIANT * v)
{
    v->vt = VT_BSTR;
    delete[] v->bstrVal;
    v->bstrVal = new char[strlen(s)];
    strcpy(v->bstrVal, s);
}

// VARIANT get value functions

template < typename T >
T VariantGetValue(VARIANT) = delete;

template <>
unsigned int VariantGetValue<unsigned int>(VARIANT Variant)
{
    if (Variant.vt != VT_UI4)
        throw std::runtime_error("bad get");
    return Variant.uintVal;
}

template <>
BSTR VariantGetValue<BSTR>(VARIANT Variant)
{
    if (Variant.vt != VT_BSTR)
        throw std::runtime_error("bad get");
    return Variant.bstrVal;
}

int main()
{
    VARIANT v;
    InitVariantFromUInt32(14, &v);

    std::cout << VariantGetValue<unsigned int>(v) << '\n';
    try {
        std::cout << VariantGetValue<BSTR>(v) << '\n';
    } catch (std::exception const& e) {
        std::cout << "Get failed!" << '\n';
    }

    VARIANT w;
    InitVariantFromString("Hello World!", &w);

    std::cout << VariantGetValue<BSTR>(w) << '\n';

  //std::cout << VariantGetValue<bool>(w) << '\n'; // error: call to deleted function 'VariantGetValue'
}
person Henri Menke    schedule 20.07.2017
comment
Вы можете delete использовать объявление по умолчанию, если хотите получить ошибку компилятора. Кроме того, что это за тип T в специализациях? Почему вы бросаете исключение, мне тоже неизвестно. Ваши функции имеют случайную точку с запятой в конце. - person Rakete1111; 20.07.2017
comment
@ Rakete1111 Я тоже их заметил и исправил с трудом. - person GTAVLover; 20.07.2017
comment
Почему вы изменили ответ? Измененный ответ вызвал ошибки. - person GTAVLover; 20.07.2017
comment
@ Rakete1111 Спасибо за ваш вклад. Я исправил свой код. Исключение необходимо, потому что VARIANT — это всего лишь объединение с тегами, поэтому получение значения всегда будет успешным, но будет мусором, если сохраненный и извлеченный тип не совпадают. Я сделал ошибку, скопировав большую часть кода из вопроса. - person Henri Menke; 20.07.2017
comment
@GTAVLover Нет, исходный ответ никогда бы не скомпилировался. В обновленном ответе вы должны сами заполнить проверки типов (я понятия не имею, как из типов С++ в VARTYPE), тогда он скомпилируется. - person Henri Menke; 20.07.2017
comment
@HenriMenke, что вы имели в виду под (/* check that Variant stores... в своем ответе? - person GTAVLover; 20.07.2017
comment
@HenriMenke Спасибо! Я попробую сам. - person GTAVLover; 20.07.2017
comment
@ Анри Имеет смысл, это состояние сбило меня с толку. Кроме того, под удалением я не подразумевал полное удаление :) Основной шаблон все еще должен существовать: template<typename T> T VariantGetValue(VARIANT value) = delete; - person Rakete1111; 20.07.2017
comment
@GTAVLover Я добавил код для случая unsigned int, но понятия не имею, работает ли он. Я только что нашел эту функцию в MSDN. . - person Henri Menke; 20.07.2017
comment
@HenriMenke Спасибо за это, но теперь я продолжаю получать ошибку IntelliSense: function "VariantGetValue<T>(VARIANT) [with T = unsigned int]" (declared at line X) cannot be referenced -- it is a deleted function.? Как я могу использовать эту функцию. В отличие от того, что я использовал? Отличается ли использование? Я пытался использовать то же, что и раньше. - person GTAVLover; 20.07.2017
comment
Я использовал ваш второй ответ. Когда я использую такую ​​​​функцию, как return VariantGetValue<unsigned int>(CV);, я получаю ошибку выше. Почему вы использовали ключевое слово delete с шаблоном в своем ответе? Что оно делает? - person GTAVLover; 20.07.2017
comment
@ Rakete1111 Какова цель ключевого слова delete здесь? - person GTAVLover; 20.07.2017
comment
@GTAVLover Я добавил в ответ полный пример. Если у вас есть дополнительные вопросы, возможно, попробуйте прочитать руководство. - person Henri Menke; 20.07.2017