Convert std::variant to another std::variant with super-set of types
This is an implementation of Yakk's second option:
template <class... Args>
struct variant_cast_proxy
{
std::variant<Args...> v;
template <class... ToArgs>
operator std::variant<ToArgs...>() const
{
return std::visit([](auto&& arg) -> std::variant<ToArgs...> { return arg ; },
v);
}
};
template <class... Args>
auto variant_cast(const std::variant<Args...>& v) -> variant_cast_proxy<Args...>
{
return {v};
}
You might want to fine tune it for forwarding semantics.
And as you can see its use is simple:
std::variant<int, char> v1 = 24;
std::variant<int, char, bool> v2;
v2 = variant_cast(v1);
Casting a variant to super set variant or a subset variant
You can add a static_assert
checking if any of the possibly-held variants are convertible:
static_assert((std::is_convertible_v<Args, std::variant<ToArgs...>> || ...),
"No possible variant that could be converted exists");
Or if you want SFINAE, you can do it in the template arguments:
// extracted into helper function
template <class... ToArgs>
static constexpr bool is_convertible() noexcept {
return (std::is_convertible_v<Args, std::variant<ToArgs...>> || ...);
}
template<class... ToArgs, std::enable_if_t<is_convertible<ToArgs...>(), int> = 0>
operator std::variant<ToArgs...>() const
{
// ...
}
Convert `std::any` to `std::variant`
This code takes a std::any
object along with a list of types and converts the object to std::variant
or throws std::bad_any_cast
if the stored type is not one of the given types.
#include <any>
#include <variant>
#include <optional>
#include <typeinfo>
template <class... Args>
auto any_to_variant_cast(std::any a) -> std::variant<Args...>
{
if (!a.has_value())
throw std::bad_any_cast();
std::optional<std::variant<Args...>> v = std::nullopt;
bool found = ((a.type() == typeid(Args) && (v = std::any_cast<Args>(std::move(a)), true)) || ...);
if (!found)
throw std::bad_any_cast{};
return std::move(*v);
}
Example usage:
auto test(const std::any& a)
{
auto v = any_to_variant_cast<int, std::string>(a);
std::visit([](auto val) { std::cout << val << std::endl; }, v);
}
Code on godbolt
Some explanations:
std::optional<std::variant<Args...>>
is used because std::variant<Args...>
default constructor constructs the variant holding the value-initialized value of the first alternative and requires the first alternative to be default constructible.
((a.type() == typeid(Args) && (v = std::any_cast<Args>(std::move(a)), true)) || ...)
// ------------------------ -------------------------------------
// type_check any_cast
This is a fold expression. I've renamed some of the subexpression to be easier to explain. With the renaming the expression becomes:
// ((type_check && (any_cast, true)) || ...)
- if
type_check
isfalse
then:(any_cast, true)
is not evaluated due to the short circuit of&&
(type_check && (any_cast, true))
evaluates tofalse
- the next op in the fold expression is evaluated
- if
type_check
istrue
then:(any_cast, true)
is evaluated:any_cast
is evaluated. The variant get the value from the any object.any_cast
result is discardedtrue
is evaluated(any_cast, true)
evaluates totrue
(type_check && (any_cast, true))
evaluates totrue
- the rest of the fold is not evaluated due to the short circuit of
||
- the whole expression (the fold) evaluates to
true
- if no
type_check
evaluates totrue
then the whole expression (the fold) evaluates tofalse
dealing with variants containing move-only types
template<template<class...>class, class> struct is_instance_of:std::false_type{};
template<template<class...>class Z, class...Ts> struct is_instance_of<Z,Z<Ts...>>:std::true_type{};
template<template<class...>class Z, class T>
constexpr bool is_instance_of_v=is_instance_of<Z,T>::value;
flattened_variant flatten(unflattened_variant const& v) {
return std::visit([](auto const& e)->flattened_variant{
using T = std::decay_t<decltype(e)>;
if constexpr (is_instance_of_v<std::unique_ptr, T>){
return *e;
else
return e;
}, v);
}
we add a trait to dispatch on, then use if constexpr to have 2 function bodies.
In c++20 we have lots more options.
[]<class T>(T const& e)->flattened_variant{
if constexpr (is_instance_of_v<std::unique_ptr, T>){
Then, going back to overloading solution, we have:
[]<class T>(std::unique_ptr<T> const&)
or
template<class T, template<class...>class Z>
concept instance_of=is_instance_of<Z,T>::value;
then
[](instance_of<std::unique_ptr> auto const& e)
or
[]<<instance_of<std::unique_ptr> T>(T const& e)
Prior to c++17 in c++14 we can use a dispatch helper:
template<class T0, class T1>
constexpr T0&& constexpr_branch( std::true_type, T0&& t0, T1&& ) { return std::forward<T0>(t0); }
template<class T0, class T1>
constexpr T1&& constexpr_branch( std::false_type, T0&&, T1&& t1 ) { return std::forward<T1>(t1); }
flattened_variant flatten(unflattened_variant const& v) {
return std::visit([](auto const& e)->flattened_variant{
using T = std::decay_t<decltype(e)>;
return constexpr_branch(
is_instance_of<std::unique_ptr, T>,
[](auto const& e){return *e;},
[](auto const& e){return e;}
)(e);
}, v);
}
going back to c++11 (where did you get your variant?), you could make an external class:
template<class R>
struct flatten {
template<class T>
R operator()(std::unique_ptr<T> const& p)const{
return *p;
}
template<class T>
R operator()(T const& v)const{
return v;
}
};
then just do a
return std::visit( flatten<flattened_variant>{}, v );
Conversion of variant, vectorvariant and vectorvectorvariant to the equivalent type of my choice
I would suggest the following to get the types you want:
template<typename T>
class converter_visitor : public boost::static_visitor<>
{
public:
std::vector<T>& vec;
converter_visitor(std::vector<T>& r) : vec(r) {}
// only push back values of specific types...
void operator()(const T& u) const {
vec.push_back(u);
}
// ignore other types...
void operator()(...) const {}
};
template<typename T>
converter_visitor<T> make_visitor(std::vector<T>& r) { return converter_visitor<T>(r); }
and then funnel that to a recursive filter func that can handle nested vectors:
template<typename T,typename U>
void filter(std::vector<T>& result,const U& var) {
boost::apply_visitor( make_visitor(result), var );
}
template<typename T,typename U>
void filter(std::vector<T>& result,const std::vector<U>& cont) {
std::for_each(cont.begin(),cont.end(),[&](const U& c) {
filter(result,c);
});
}
then you can do:
_var v = 314;
std::vector<int> result;
filter(result,v);
print(result);
result: 314
_vec_var v;
v.push_back(2);
v.push_back(3);
v.push_back("hello");
v.push_back(5);
v.push_back(7);
v.push_back("world");
std::vector<int> result;
filter(result,v);
print(result);
std::vector<std::string> result2;
filter(result2,v);
print(result2);
result1: 2 3 5 7
result2: hello world
_vec_var v1;
v1.push_back(11);
v1.push_back(13);
v1.push_back("see ya");
_vec_var v2;
v2.push_back(17);
v2.push_back(19);
v2.push_back("later");
_vec2_var vv;
vv.push_back(v1);
vv.push_back(v2);
std::vector<int> result;
filter(result,vv);
print(result);
std::vector<std::string> result2;
filter(result2,vv);
print(result2);
result1: 11 13 17 19
result2: see ya later
see live demo here
A simple way to convert to/from VARIANT types in C++
Well, most of the hard work is already done for you with the various wrapper classes. I prefer _variant_t and _bstr_t as they are more suited for conversion to/from POD types and strings. For simple arrays, all you really need is template conversion function. Something like the following:
// parameter validation and error checking omitted for clarity
template<typename T>
void FromVariant(VARIANT Var, std::vector<T>& Vec)
{
CComSafeArray<T> SafeArray;
SafeArray.Attach(Var.parray);
ULONG Count = SafeArray.GetCount();
Vec.resize(Count);
for(ULONG Index = 0; Index < Count; Index++)
{
Vec[Index] = SafeArray[Index];
}
}
....
std::vector<double> Vec;
VARIANT Var = ...;
FromVariant(Var, Vec);
...
Of course things get hairy (in regards to memory / lifetime management) if the array contains non-POD types, but it is still doable.
I'm working on heterogenous list that uses std::variant. Is there a way to initialize it every time with a custom set of types for std::variant?
- Is this just a bad idea of using std::variant in this case?
There is too little context, so I have to assume that you are using std::variant
because std::variant
is what you need.
- If yes, should I use templates or std::any?
std::any
is for something very different. std::variant
is for a limited set of types, std::any
is for any type. Pick one or the other depending on what you actully need, not based on easier refactoring or the like.
- Is there a way to store types and pass them into HVector during initialization?
You can use an alias:
using my_variant = std::variant<Party,Loot>;
If you like you can make the classes templates and provide the variant type as parameter:
template <typename T>
class HVector:
{
public:
HVector();
void push(T newData);
void show();
};
this will be more flexible but isn't really necessary.
- Are refactoring tools a solution to the problem?
Once you have the alias my_variant
and you use that consistently throughout your code, you only have to change it in one single place.
PS: Publicly inheriting from standard containers is often not a good idea. It can be done right, but you have to be careful. I allowed myself to simply ignore that part in above example, because it is only remotely related to the question.
PPS: The situation with std::variant
is a bit similar to std::pair
. What I mean is that genericity comes at the price of not meaningful names. Even if you use that variant in only one single place in your code, you should give it a meaningful name. When reading your code, I don't understand what a std::variant<Party,Loot>
really is. Would be different with an alias that tells me something about the meaning. I didn't know what is a good name. my_variant
certainly isn't one.
Related Topics
What Is the Cin Analougus of Scanf Formatted Input
What Does the & (Ampersand) at the End of Member Function Signature Mean
Vector of Class Without Default Constructor
Why Does Makeintresource() Work
Calling Erase with Iterator VS Const_Iterator
C++ Virtual Function Table Memory Cost
How to Decide If a Template Specialization Exist
Sorting a List of a Custom Type
How to Properly Delete a Pointer to Array
Why Does Long Long 2147483647 + 1 = -2147483648
How Does the Friend Keyword (Class/Function) Break Encapsulation in C++
How to Read a File at Compile Time
What Is the Purpose of Unary Plus Operator on Char Array
C++ Get Description of an Exception Caught in Catch(...) Block
Why Does C++ Allow Private Members to Be Modified Using This Approach
What Does Afx_Manage_State(Afxgetstaticmodulestate()) Do Exactly