运行时如何获取std::variant包含的对象?

问题描述 投票:0回答:1

我正在尝试实现一个模板化的请求-响应日志类。

这是一个用法示例:

struct RequestA {
    std::string data;
};

struct ResponseA {
    int code;
};

struct RequestB {
    int data;
};

struct ResponseB {
    double value;
};

int main(int argc, char* argv[])
{
    constexpr std::size_t maxEntries = 5;
    RequestJournal<maxEntries, std::pair<RequestA, ResponseA>, std::pair<RequestB, ResponseB>> requestJournal{ 1000 };

    auto requestA = std::make_shared<RequestA>(RequestA{ "RequestA data"});
    requestJournal.addRequest(0, requestA);

    auto requestB = std::make_shared<RequestB>(RequestB{ 10 });
    requestJournal.addRequest(1, requestB);
}

想法是拥有一个不知道请求/响应类型的基础设施类,在创建时注册所有可能的请求-响应对,并能够为请求定义特定的插槽。

以下是一个原型(基于这种方法link):


inline constexpr std::size_t npos = -1;

template <typename T, typename... Ts>
struct index : std::integral_constant<std::size_t, npos> {};

template <typename T, typename... Ts>
inline constexpr std::size_t index_v = index<T, Ts...>::value;

template <typename T, typename... Ts>
struct index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};

template <typename T, typename Head, typename... Tail>
class index<T, Head, Tail...>
{
    static constexpr std::size_t tmp = index_v<T, Tail...>;

public:
    static constexpr std::size_t value = tmp == npos ? tmp : tmp + 1;
};

// Helper function that gets the variant type information for a specific type
template <typename T, typename... Ts>
std::pair<const std::type_info*, std::any> GetVariantTypeInfo(std::variant<Ts...>& variant)
{
    // Get the index of the specified type in the variant
    constexpr static auto indexOfType = index_v<T, Ts...>;

    // Check if the specified type is currently stored in the variant
    if (indexOfType == variant.index())
    {
        auto obj = std::get<indexOfType>(variant);

        // Get the type information and object for the specified type
        return std::make_pair(&typeid(obj), std::any(obj));
    }

    // Return a null pointer to indicate that the type information was not found
    return std::make_pair(nullptr, std::any());
}

// Helper function that calls GetVariantTypeInfo for each type in a parameter pack
template <typename... Ts>
const std::type_info* GetVariantTypeInfo(std::variant<Ts...>& variant)
{
    // Call GetVariantTypeInfo for each type in the parameter pack using fold expression
    const std::initializer_list<std::pair<const std::type_info*, std::any>> typeInfos = { GetVariantTypeInfo<Ts, Ts...>(variant)... };
    for (const auto& typeInfo : typeInfos)
    {
        if (typeInfo.first != nullptr)
        {
            const auto& typeIdx = *typeInfo.first;
            return typeInfo.first;
        }
    }
    return nullptr;
}

template <std::size_t maxEntries, typename... Pairs>
class RequestJournal {
public:
    using EntryIndex = std::size_t;

    using RequestTypesVariant = std::variant<std::shared_ptr<typename Pairs::first_type> ...>;
    using ResponseTypesVariant = std::variant<std::shared_ptr<typename Pairs::second_type> ...>;

    template <typename T>
    static constexpr bool request_is_in_pack = (std::is_same_v<T, typename Pairs::first_type> || ...);

    template <typename T>
    static constexpr bool response_is_in_pack = (std::is_same_v<T, typename Pairs::second_type> || ...);

    RequestJournal(int latencyMsec) {
        m_latency = std::chrono::milliseconds(latencyMsec);
    }

    template <typename T>
    std::enable_if_t<response_is_in_pack<T>, void> setResponse(EntryIndex index, std::shared_ptr<T> response) {

        const auto requestTypeInfo =
            GetVariantTypeInfo<std::shared_ptr<typename Pairs::first_type>...>
            (m_journal[index].requestContainer);
     
        // other code ...
    }

private:
    using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;

    struct RequestEntry {
        RequestTypesVariant requestContainer;
        ResponseTypesVariant responseContainer;
    };

    RequestJournal() {}

    std::size_t i = 0;
    std::unordered_map<std::size_t, std::pair<std::type_index, std::type_index>> m_pairsMap{
        { i++, std::make_pair(std::type_index(typeid(typename Pairs::first_type)), std::type_index(typeid(typename Pairs::second_type)))}...
    };

    std::chrono::milliseconds m_latency;
    std::array<RequestEntry, maxEntries> m_journal;
};

问题是我如何(如果可能的话)将变体本身 (

auto obj = std::get<indexOfType>(variant);
) 中的对象传播到
setResponse
函数。

c++ variadic-templates
1个回答
3
投票

您需要简化代码并使用

std::visit
。这是一个简单的调度响应版本,使用
std::variant
https://godbolt.org/z/jTYj8o13e


#include <variant>
#include <string>
#include <vector>
#include <iostream>

struct request_a {};
struct request_b{};

struct response_a{
    std::string value;
};
struct response_b{
    int value;
};

response_a respond(request_a) {
    std::cout << "responding to request_a\n";

    return {"oceanic"};
} 

response_b respond(request_b) {
    std::cout << "responding to request_b\n";

    return {815};
} 

int main() {

    std::vector<std::variant<request_a, request_b>> requests{request_a(), request_b()};

    for (const auto &req : requests)
    {
        std::visit([](const auto &req){
            std::cout << respond(req).value << '\n';
        }, req);
    }

    return 0;
}

输出:

responding to request_a
oceanic
responding to request_b
815
© www.soinside.com 2019 - 2024. All rights reserved.