我正在尝试实现一个模板化的请求-响应日志类。
这是一个用法示例:
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
函数。
您需要简化代码并使用
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