варианты в современном с ++, как лучше их сделать - PullRequest
0 голосов
/ 31 марта 2020

Переходя от парадигмы C к современной C ++, я пытаюсь выяснить, какой может быть лучшая замена C ++ для способа представления вариантов COM или устройства более старого стиля: PROPVARIANT , и DEVPROPERTY .

В смысле C эти варианты являются просто структурой с: ключом (GUID, пара PID), типом данных (который является просто UIN32) и буфер.

typedef struct _DEVPROPERTY {
  DEVPROPCOMPKEY CompKey;
  DEVPROPTYPE    Type;
  ULONG          BufferSize;
  PVOID          Buffer;
} DEVPROPERTY, *PDEVPROPERTY;

С их помощью PnP и старые COM-компоненты типа Shell могут передавать данные свойств в общем виде.

Я знаю, что могу просто повторно использовать эти C структуры в c ++, но Как можно сделать это с современным C ++?

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

Существует также std :: any , который выглядит как вариант std ::, но без предопределения типов.

В любом случае, я не уверен, как загрузить данные и заново открыть тип данных непосредственно с ними. Я думаю, я мог бы просто иметь структуру с типом, определенным как enum, и использовать std: вариант вместо объединения.

Например, мне все еще нужно иметь возможность сделать что-то вроде этого:

void DoSomething(DEVPROPERTY* prop)
{
    // assume we would be doing safe things with these pointers instead of directly derefing them and casting them :)
    switch (prop->Type) {
    case DEVPROP_TYPE_INT32:
        LONG val = *(LONG*)prop->Buffer;
        break;

    }
}

1 Ответ

0 голосов
/ 01 апреля 2020

std::variant и std::visit обычно должны быть вашими go, чтобы отвечать за подобные вещи. Они обеспечивают безопасный тип доступа к содержащемуся значению, которое не может обеспечить хороший старый тип enum и тип void*.

Например, функция DoSomething из вашего вопроса может быть записана как-то как это:

// Bit of boilerplate for combining lambdas
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

void DoSomething(const std::variant<long, std::string>& prop)
{
    std::visit(
        overloaded {
            [](long lng) { /* do whatever */ },
            [](const std::string& strng) { /* do whatever */ }
        },
        prop
    )
}

Правильная лямбда будет вызываться для любого типа, содержащегося в prop, и вы получите ошибку времени компиляции, если prop может содержать тип, который не является обрабатываются. Нет указателей приведения и не забудьте добавить новый обработчик, если добавлен новый тип свойства.


std::any гораздо ближе к проверенному типу void*, чем к варианту. Хотя он обеспечивает некоторую защиту времени выполнения от доступа к его значению как неправильному типу, он не может обеспечить защиту времени компиляции .

std::variant сродни C построить как это:

struct variant {
    enum {
        INT,
        STRING
    } type;
    union {
        int i;
        char* c;
    }
};

В то время как std::any больше похоже на C построить как это:

struct any {
    char* type;
    void* value;
};
...