I am trying to implement a templated request-response journal class.
This is a usage example:
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);
}
The idea is to have an infrastructure class which is not aware about request/response types, to register all possible request-response pairs upon creation and to be able to define specific slot for a request.
The following is a prototype (based on this approach 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;
};
The question is how (and if it is possible at all) can I propagate the object within a variant itself (auto obj = std::get<indexOfType>(variant);
) to the setResponse
function.