I built a container that can hold objects of various types, and look up an object convertible to a given type. I wrote something about my intended use-case here:
Basically I want to be able to throw a bunch of dependencies onto a pile and then grab the exact version I want from calling code -- for instance, if I just need an IWidget, I can do
IWidget * widget = dependencies.get<IWidget *>();
but if the underlying widget is actually a FancyGreenWidget then I can do
FancyGreenWidget * widget = dependencies.get<FancyGreenWidget *>();
and if it's not fancy and green then I'll get a helpful runtime error during program initialization rather than a surprise segfault sometime while the program is running.
I'm not really familiar with C++17, but it seemed like I needed a bunch of C++17 features to implement my use-case. I feel like someone who writes modern C++ on a daily basis can probably glance at this and tell me several things I'm doing wrong. In particular I suspect there's a much cleaner way to write the visitor in the get<T>()
method.
template<class... Types>
class VariantContainer
{
private:
std::vector<std::variant<Types...>> m_values;
public:
template<class T>
void add(const T &t)
{
static_assert(std::disjunction_v<std::is_same<T, Types>...>);
m_values.push_back(t);
}
template<class T>
T get()
{
for(int i = 0; i < m_values.size(); i++)
{
std::optional<T> result = std::visit(
[](auto&& arg) {
using Targ = std::decay_t<decltype(arg)>;
if constexpr(std::is_convertible_v<Targ, T>)
{
return std::optional<T>(static_cast<T>(arg));
}
else
{
return std::optional<T>();
}
},
m_values[i]);
if(result.has_value()) { return result.value(); }
}
std::string message = "Could not find member of type ";
message += abi::__cxa_demangle(typeid(T).name(), 0, 0, nullptr);
throw std::runtime_error(message);
}
};
Sample usage:
VariantContainer<Base *, Derived1 *, Derived2 *> v;
Derived2 d;
v.add(&d);
Base * b = v.get<Base *>(); // returns a pointer to d
Derived1 * d1 = v.get<Derived1 *>(); // ERROR
Derived2 * d2 = v.get<Derived2 *>(); // returns a pointer to d
I'm also in the market for a more descriptive name for this class if anyone has any ideas.
float
todouble
,double
toint
, or more nefarious ones like this example )? \$\endgroup\$ – hoffmale Oct 26 '19 at 12:23