I'm trying to write a logic, with usage of command and factory patterns. Based on a data inside the std::variant
I want to create an object. It may look like a map of C++ types.
Here is the code: Wandbox
I can hardcode all used types but I want to automate it with variadic templates, how to do it?
#include <cassert>
#include <variant>
#include <type_traits>
#include <memory>
#include <iostream>
struct IFace
{
virtual void foo() = 0;
};
template<typename TKey, typename TValue>
struct Base : IFace
{
using Key = TKey;
using Value = TValue;
};
struct Int : Base<int, Int>
{
void foo() override { std::cout << "Int"; }
};
struct Double : Base<double, Double>
{
void foo() override { std::cout << "Double"; }
};
using Var = std::variant<int, double>;
template<typename ...Args>
std::shared_ptr<IFace> factory(const Var& v)
{
std::shared_ptr<IFace> p;
std::visit([&p](auto&& arg){
using TKey = std::decay_t<decltype(arg)>;
// TODO: use variadic instead of hardcoded types
if constexpr (std::is_same_v<TKey, int>)
{
using TValue = typename Base<TKey, Int>::Value;
p = std::make_shared<TValue>();
std::cout << "int ";
}
else if constexpr (std::is_same_v<TKey, double>)
{
using TValue = typename Base<TKey, Double>::Value;
p = std::make_shared<TValue>();
std::cout << "double ";
}
}, v);
return p;
}
int main()
{
const Var v = 42;
auto p = factory<Int, Double>(v);
assert(p != nullptr);
p->foo();
return 0;
}
This popular little "overloaded" class is always useful for passing lambdas to std::visit
:
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Then, instead of using a generic lambda and trying to deduce the right object type from the parameter type, just generate an overload for each Args
.
template<typename ...Args>
std::shared_ptr<IFace> factory(const std::variant<typename Args::Key...>& v)
{
return std::visit(overloaded {
[](const typename Args::Key&) -> std::shared_ptr<IFace> { return std::make_shared<Args>(); }...
}, v);
}