How do boost::variant and boost::any work?
If you read the boost::any documentation they provide the source for the idea: http://www.two-sdg.demon.co.uk/curbralan/papers/ValuedConversions.pdf
It's basic information hiding, an essential C++ skill to have. Learn it!
Since the highest voted answer here is totally incorrect, and I have my doubts that people will actually go look at the source to verify that fact, here's a basic implementation of an any like interface that will wrap any type with an f() function and allow it to be called:
struct f_any
{
f_any() : ptr() {}
~f_any() { delete ptr; }
bool valid() const { return ptr != 0; }
void f() { assert(ptr); ptr->f(); }
struct placeholder
{
virtual ~placeholder() {}
virtual void f() const = 0;
};
template < typename T >
struct impl : placeholder
{
impl(T const& t) : val(t) {}
void f() const { val.f(); }
T val;
};
// ptr can now point to the entire family of
// struct types generated from impl<T>
placeholder * ptr;
template < typename T >
f_any(T const& t) : ptr(new impl<T>(t)) {}
// assignment, etc...
};
boost::any does the same basic thing except that f() actually returns typeinfo const&
and provides other information access to the any_cast function to work.
Boost.Any vs. Boost.Variant
Have you looked at the comparison in the variant library already?
(Not sure what states from external sources are, so it's kind of hard to say what's more appropriate for you.)
Is it possible to use boost::any or boost::variant with a boost::pool?
Yes, boost pool works with both. You're simply using it wrong.
CAVEAT: There's really no use at all to use a pool allocator with
boost::any
because it will dynamically allocate the held value outside of the pool.But this doesn't mean that you can't if you use it right.
malloc
only allocates uninitialized memory. It's your error to expect to be able to assign to it as if it were a fully functional instance of the object type that the point implies.
T *i = pool.malloc();
new (i) T();
This fixes it:
Live On Coliru
#include <boost/pool/object_pool.hpp>
#include <boost/any.hpp>
#include <boost/variant.hpp>
template <typename T>
void run_test() {
boost::object_pool<T> pool;
T *i = pool.malloc();
new (i) T();
*i = 1;
T *j = pool.construct(2);
pool.destroy(i);
pool.destroy(j);
}
int main() {
run_test<boost::variant<int, double> >();
run_test<boost::any>();
}
This also runs clean under asan/ubsan and valgrind.
Bonus Question
Is this expected behavior? Does the boost::pool only work for simple C++ types like int, doble, float, etc?
For POD types or trivial types you could get away with eliding the constructor, much like the C++ compiler is allowed to elide them in those cases.
Boost Any to Boost Variant using Boost Preprocessor
Here you go:
#define ANY_TO_VARIANT_OP_VARIANT(typeSeq) \
boost::optional<boost::variant<BOOST_PP_SEQ_ENUM(typeSeq)>>
#define ANY_TO_VARIANT_CONVERT_AND_RETURN(r, data, elem) \
if (any.type() == typeid(elem)) { \
return Ret{boost::any_cast<elem>(any)}; \
}
#define SPECIALIZE_BOOST_ANY_TO_VARIANT(typeSeq) \
template<> \
ANY_TO_VARIANT_OPT_VARIANT(typeSeq) anyToVariant(const boost::any& any) { \
using Ret = ANY_TO_VARIANT_OPT_VARIANT(typeSeq); \
BOOST_PP_SEQ_FOR_EACH(ANY_TO_VARIANT_CONVERT_AND_RETURN, ~, typeSeq) \
return Ret{}; \
}
Usage:
SPECIALIZE_BOOST_ANY_TO_VARIANT((int)(double)(std::string))
See it live on Coliru
How to properly replace boost::variant by std::variant?
As you have duplicate types in your variant some of the constructors are disabled:
This overload only participates in overload resolution if there is exactly one occurrence of T in Types...
You need to use the constructor with an explicit type index:
return std::variant<T...>(std::in_place_index<n>, std::get<n>(tpl));
Your original boost code has undefined behaviour:
Each type specified as a template argument to variant must be distinct after removal of qualifiers. Thus, for instance, both
variant<int, int>
andvariant<int, const int>
have undefined behavior.
The standard libary implementation does support duplicate types and therefore prevents you from accidentally constructing an ambigous variant. For example what should the following do:
variant<std::string, double, double, int> t = 4.5;
With boost it is UB, either of the double
values might be initialised or it might do something completely different. The standard library explitly makes this a compiler error so that you have to choose which of your double
s you want to initialise.
What are the differences between std::variant and boost::variant?
Assignment/emplacement behavior:
boost::variant
may allocate memory when performing assignment into a livevariant
. There are a number of rules that govern when this can happen, so whether aboost::variant
will allocate memory depends on theTs
it is instantiated with.std::variant
will never dynamically allocate memory. However, as a concession to the complex rules of C++ objects, if an assignment/emplacement throws, then thevariant
may enter the "valueless_by_exception" state. In this state, thevariant
cannot be visited, nor will any of the other functions for accessing a specific member work.You can only enter this state if assignment/emplacement throws.
Boost.Variant includes
recursive_variant
, which allows avariant
to contain itself. They're essentially special wrappers around a pointer to aboost::variant
, but they are tied into the visitation machinery.std::variant
has no such helper type.std::variant
offers more use of post-C++11 features. For example:It forwards the
noexcept
status of the special member functions of its constituent types.It has variadic template-based in-place constructors and emplacement functions.
Defect resolutions applied to C++17 may mean that it will also forward trivial copyability of its types. That is, if all of the types are trivially copyable, then so too will
variant<Ts>
.
Is it possible to create Boost multi_index MEM_FUN key extractors for a container of Boost variant?
The best way to approach this is to provide your own user-defined key extractor. Note that the fact that the variant types are derived from a common base does not play any significant role here: in a scenario without inheritance, simply apply a regular visitor that takes care of all the types in the variant (possibly with a generic lambda if all the types conform to the same syntax for getting the key).
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/variant/variant.hpp>
#include <utility>
struct base
{
virtual ~base()=default;
virtual std::pair<char,char> extractKey()const=0;
};
struct derived1:base
{
std::pair<char,char> extractKey()const override{return{1,0};};
};
struct derived2:base
{
std::pair<char,char> extractKey()const override{return{0,1};};
};
using namespace boost::multi_index;
using variant=boost::variant<derived1,derived2>;
struct variant_key
{
using result_type=std::pair<char,char>;
auto operator()(const variant& x)const
{
return boost::apply_visitor(
[](const base& b){return b.extractKey();},
x
);
}
};
using container=multi_index_container<
variant,
indexed_by<
ordered_non_unique<variant_key>
>
>;
// testing
#include <iostream>
template<typename... Ts> struct overloaded:Ts...{using Ts::operator()...;};
template<typename... Ts> overloaded(Ts...)->overloaded<Ts...>;
int main()
{
container c;
for(int i=2;i--;){
c.insert(variant(derived1()));
c.insert(variant(derived2()));
}
for(const auto& x:c){
boost::apply_visitor(
overloaded{
[](const derived1&){std::cout<<"derived1 ";},
[](const derived2&){std::cout<<"derived2 ";}
},
x
);
}
}
Output
derived2 derived2 derived1 derived1
Related Topics
Differencebetween String::At and String::Operator[]
Different Compiler Behavior for Expression: Auto P {Make_Pointer()};
How to Allocate a Specific Memory Address Using Pointers in C++
Unordered Set of Pairs, Compilation Error
Why Does Msvc Pick a Long Long as the Type for -2147483648
Compare Equality of Char[] in C
Why Does Sizeof(Int) Vary Across Different Operating Systems
Stoi and Std::To_String on Mingw 4.7.1
Image Retrieval System by Colour from the Web Using C++ with Openframeworks
Why Are Std::Atomic Objects Not Copyable
Exception Error C0000005 in Vc++
Does Clearing a Vector Affect Its Capacity
Optimizing Member Variable Order in C++