Implementing a "variant" class - c++

Note: I'm aware of boost::variant, but I am curious about the design principles. This question mostly for self-education.
Original post
At my current job I found an old variant class implementation. It's implemented with a union and can only support a handful of datatypes. I've been thinking about how one should go about designing an improved version. After some tinkering I ended up with something that seems to work. However I'd like to know your opinion about it. Here it is:
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/shared_ptr.hpp>
class Variant
{
public:
Variant() { }
template<class T>
Variant(T inValue) :
mImpl(new VariantImpl<T>(inValue)),
mClassName(typeid(T).name())
{
}
template<class T>
T getValue() const
{
if (typeid(T).name() != mClassName)
{
throw std::logic_error("Non-matching types!");
}
return dynamic_cast<VariantImpl<T>*>(mImpl.get())->getValue();
}
template<class T>
void setValue(T inValue)
{
mImpl.reset(new VariantImpl<T>(inValue));
mClassName = typeid(T).name();
}
private:
struct AbstractVariantImpl
{
virtual ~AbstractVariantImpl() {}
};
template<class T>
struct VariantImpl : public AbstractVariantImpl
{
VariantImpl(T inValue) : mValue(inValue) { }
~VariantImpl() {}
T getValue() const { return mValue; }
T mValue;
};
boost::shared_ptr<AbstractVariantImpl> mImpl;
std::string mClassName;
};
int main()
{
// Store int
Variant v(10);
int a = 0;
a = v.getValue<int>();
std::cout << "a = " << a << std::endl;
// Store float
v.setValue<float>(12.34);
float d = v.getValue<float>();
std::cout << "d = " << d << std::endl;
// Store map<string, string>
typedef std::map<std::string, std::string> Mapping;
Mapping m;
m["one"] = "uno";
m["two"] = "due";
m["three"] = "tre";
v.setValue<Mapping>(m);
Mapping m2 = v.getValue<Mapping>();
std::cout << "m2[\"one\"] = " << m2["one"] << std::endl;
return 0;
}
Output is correct:
a = 10
d = 12.34
m2["one"] = uno
My SO questions are:
Is this implementation correct?
Will the dynamic cast in getValue() work as expected (I'm not certain)
Should I return T as a const reference instead? Or can I count on return-value-optimization to kick in?
Any other problems or suggestions?
Update
Thanks to #templatetypedef for his suggestions. This updated version only uses dynamic_cast to check if the types match. Type mismatches caused by differences in constness are now avoided thanks to the TypeWrapper classes (which I have shamelessly stolen from the Poco C++ project).
So this is the current version. It's likely to contain a few errors though, as I'm not familiar with the idea of modifying const/ref on template templates. I'll have a fresh look tomorrow.
template <typename T>
struct TypeWrapper
{
typedef T TYPE;
typedef const T CONSTTYPE;
typedef T& REFTYPE;
typedef const T& CONSTREFTYPE;
};
template <typename T>
struct TypeWrapper<const T>
{
typedef T TYPE;
typedef const T CONSTTYPE;
typedef T& REFTYPE;
typedef const T& CONSTREFTYPE;
};
template <typename T>
struct TypeWrapper<const T&>
{
typedef T TYPE;
typedef const T CONSTTYPE;
typedef T& REFTYPE;
typedef const T& CONSTREFTYPE;
};
template <typename T>
struct TypeWrapper<T&>
{
typedef T TYPE;
typedef const T CONSTTYPE;
typedef T& REFTYPE;
typedef const T& CONSTREFTYPE;
};
class Variant
{
public:
Variant() { }
template<class T>
Variant(T inValue) :
mImpl(new VariantImpl<typename TypeWrapper<T>::TYPE>(inValue))
{
}
template<class T>
typename TypeWrapper<T>::REFTYPE getValue()
{
return dynamic_cast<VariantImpl<typename TypeWrapper<T>::TYPE>&>(*mImpl.get()).mValue;
}
template<class T>
typename TypeWrapper<T>::CONSTREFTYPE getValue() const
{
return dynamic_cast<VariantImpl<typename TypeWrapper<T>::TYPE>&>(*mImpl.get()).mValue;
}
template<class T>
void setValue(typename TypeWrapper<T>::CONSTREFTYPE inValue)
{
mImpl.reset(new VariantImpl<typename TypeWrapper<T>::TYPE>(inValue));
}
private:
struct AbstractVariantImpl
{
virtual ~AbstractVariantImpl() {}
};
template<class T>
struct VariantImpl : public AbstractVariantImpl
{
VariantImpl(T inValue) : mValue(inValue) { }
~VariantImpl() {}
T mValue;
};
boost::shared_ptr<AbstractVariantImpl> mImpl;
};

This implementation is close to correct, but it looks like it has a few bugs. For example, this code:
if (typeid(T).name() != mClassName)
is not guaranteed to work correctly because the .name() function in type_info is not guaranteed to return a unique value for each type. If you want to check if the types match, you should probably use something like this:
if (typeid(*mImpl) == typeid(VariantImpl<T>))
Which more accurately checks if the type matches. Of course, you need to watch out for const issues, since storing a const T and storing a T will yield different types.
As for your question about dynamic_cast, in the case you've described you don't need to use the dynamic_cast because you already have a check to confirm that the type will match. Instead, you can just use a static_cast, since you've already caught the case where you have the wrong type.
More importantly, though, what you've defined here is an "unrestricted variant" that can hold absolutely anything, not just a small set of restricted types (which is what you'd normally find in a variant). While I really like this code, I'd suggest instead using something like Boost.Any or Boost.Variant, which has been extensively debugged and tested. That said, congrats on figuring out the key trick that makes this work!

At the risk of providing a non-answer, since you are already using Boost, I recommend you try Boost.Variant or Boost.Any instead of rolling your own implementation.

Better to use std::auto_ptr, as there's no reference counting semantics required. I would normally return by reference as it's perfectly legal to change the value within, or by pointer to allow NULL.
You should use dynamic_cast to match types, not typeid(), and you could just use Boost. typeid() seems like it should provide this but in reality it doesn't because of the open-endedness of it's specification, whereas dynamic_cast is always exactly and unambiguously correct.

Related

Is it legal to specialize std library functions for a shared_ptr of a user defined type?

The standard says the following about specializing templates from the standard library (via What can and can't I specialize in the std namespace? )
A program may add a template
specialization for any standard library template to namespace std only
if the declaration depends on a user-defined type and the
specialization meets the standard library requirements for the
original template and is not explicitly prohibited.
Is it legal to specialize standard library templates with a standard library class specialized with a user defined class?
For example, specializing std::hash for std::shared_ptr<MyType>?
From reading the above paragraph and linked question, it sounds like it should be, as the declaration of the specialization is dependent on MyType, however "Unless explicitly prohibited" worries me slightly.
The example below compiles and works as expected (AppleClang 7.3), but is it legal?
#include <unordered_set>
#include <memory>
#include <cassert>
#include <string>
struct MyType {
MyType(std::string id) : id(id) {}
std::string id;
};
namespace std {
template<>
struct hash<shared_ptr<MyType>> {
size_t operator()(shared_ptr<MyType> const& mine) const {
return hash<string>()(mine->id);
}
};
template<>
struct equal_to<shared_ptr<MyType>> {
bool operator()(shared_ptr<MyType> const& lhs, shared_ptr<MyType> const& rhs ) const {
return lhs->id == rhs->id;
}
};
}
int main() {
std::unordered_set<std::shared_ptr<MyType>> mySet;
auto resultA = mySet.emplace(std::make_shared<MyType>("A"));
auto resultB = mySet.emplace(std::make_shared<MyType>("B"));
auto resultA2 = mySet.emplace(std::make_shared<MyType>("A"));
assert(resultA.second);
assert(resultB.second);
assert(!resultA2.second);
}
Yes, that is legal.
It is even questionably legal to specialize for std::shared_ptr<int> at one point; I don't know if they patched that ambiguity in the standard as a defect or not.
Note that that is a poor implemenation of a hash for global use. First, because it doesn't support null shared pointers. Second, because hashing a shared pointer as always the int value is questionable. It is even dangerous, because if a shared pointer to an int in a container has that int change, you just broke the program.
Consider making your own hasher for these kind of cases.
namespace notstd {
template<class T, class=void>
struct hasher_impl:std::hash<T>{};
namespace adl_helper {
template<class T>
std::size_t hash( T const& t, ... ) {
return ::notstd::hasher_impl<T>{}(t);
}
};
namespace adl_helper2 {
template<class T>
std::size_t hash_helper(T const& t) {
using ::notstd::adl_helper::hash;
return hash(t);
}
}
template<class T>
std::size_t hash(T const& t) {
return ::notstd::adl_helper2::hash_helper(t);
}
struct hasher {
template<class T>
std::size_t operator()(T const& t)const {
return hash(t);
}
};
}
Now this permits 3 points of customization.
First, if you override std::size_t hash(T const&) in the namespace containing T, it picks it up.
Failing that, if you specialize notstd::hasher_impl<T, void> for your type T, it picks it up.
Third, if both of those fail, it invokes std::hash<T>, picking up any specializations.
Then you can do:
std::unordered_set<std::shared_ptr<MyType>, ::notstd::hasher> mySet;
and add:
struct MyType {
MyType(std::string id) : id(id) {}
std::string id;
friend std::size_t hash( MyType const& self) {
return ::notstd::hash(self.id);
}
friend std::size_t hash( std::shared_ptr<MyType> const& self) {
if (!self) return 0;
return ::notstd::hash(*self);
}
};
which should give you a smart hash on on shared_ptr<MyType>.
This keeps the danger that someone changes id on a shared_ptr<MyType> which breaks every container containing the shared_ptr<MyType> in a non-local manner.
Shared state is the devil; consider writing a copy on write pointer if you are really worried about copying these things being expensive.

Polymorphic setter for Boost::variant

I am trying to use the boost::variant with template types. For example, I have a template type Tag<T> and the boost::variant AnyTag comprises types such as Tag<double>, Tag<int> and Tag<std::string>. Each Tag<T> has members of type T.
Now, I would like to put those variants in a container and simply assign values during runtime, e.g.,
for(AnyTag & tag: AllTags) {
setValue(tag, getValueFromXml());
}
The function setValue(AnyTag &tag, T &val) must use the runtime type of the AnyTag tag in order to correctly assign the tag with the correct value.
My attempt to solving the problem can be found below and it makes use of another variant which included only the possible T types that could be used in the AnyTag (TagValueType).
template<typename T, typename = void>
class Tag {};
template <typename T>
class Tag<T, EnableIf<std::is_arithmetic<T>>> {
public:
T value = 0;
std::string address = "";
T maxValue = std::numeric_limits<T>::max();
typedef T value_type;
};
template <typename T>
class Tag<T, DisableIf<std::is_arithmetic<T>>> {
public:
T value;
std::string address = "";
typedef T value_type;
};
typedef boost::variant<Tag<std::string>,
Tag<double>,
Tag<int>,
> AnyTag;
typedef boost::variant<std::string, double, int> TagValueType;
class tag_set_value_visitor: public boost::static_visitor<void>
{
const TagValueType & value;
public:
tag_set_value_visitor(const TagValueType & val): value(val){}
template <typename T>
void operator()(T & tag) const
{
tag.value = boost::get<typename T::value_type>(value);
}
};
inline void setValue(AnyTag & tag, const TagValueType & val) {
assert(tag.which() == val.which());
boost::apply_visitor( tag_set_value_visitor(val), tag );
}
Unfortunately, this approach is not what I would like because for example during compilation there is not problem if I do the following:
AnyTag a = Tag<int>();
setValue(a, double(1.3));
but during runtime, the boost library detects the type mismatch and crashes the program.
So, my solution is kind of a type erasure that just postpones the problem.
What I would like to have is a setValue(AnyTag &tag, T &val) where T is the runtime type of the AnyTag.
I get that that's what the variant's visitor tries to do, but there is a problem in this case because when we construct the visitor we must know the type that we are going to use.
Any ideas or any thoughts about this problem?
P.S.: Sorry for the long post but I couldn't find a way to explain my thought process with fewer words.
Use¹ a binary visitor.
Implement the operator() to do nothing except for corresponding types.
Handle mismatches to taste (I return a boolean indicating success):
Live On Coliru
#include <boost/any.hpp>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <string>
using namespace boost;
template <typename T>
struct Tag {
T value;
};
using Types = mpl::vector<std::string, double, int>;
using Tags = mpl::transform<Types, Tag<mpl::_1> >::type;
using Variant = make_variant_over<Types>::type;
using AnyTag = make_variant_over<Tags>::type;
namespace mydetail {
struct assign_to : boost::static_visitor<bool> {
template <typename V> bool operator()(Tag<V>& tagged, V const& value) const {
tagged.value = value;
return true;
}
template <typename T, typename V> bool operator()(T&&, V&&) const {
return false;
}
};
}
bool setValue(AnyTag &tag, Variant const& val) {
return boost::apply_visitor(mydetail::assign_to(), tag, val);
}
int main() {
AnyTag t;
t = Tag<std::string>();
// corresponding type assigns and returns true:
assert(setValue(t, "yes works"));
// mismatch: no effect and returns false:
assert(!setValue(t, 42));
assert(!setValue(t, 3.1415926));
}
¹ If I understood your goal correctly. I've focused on the "What I would like to have is a setValue(AnyTag &tag, T &val) where T is the runtime type of the AnyTag." part of the request.

Variant implementation like boost::any with auto-conversion support

I want to implement a variant class that can store any datatype (like boost::any) but with the support of datatype conversion. For example,
Variant v1(int(23)); can be converted to bool via v1.get<bool>()
using Conv<int, bool>, Variant v2(CustomT1()); to CustomT2
via Conv<CustomT1, CustomT2> and so on.
Here is the current implementation, based on the idea of boost::any:
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
template<typename Src, typename Dest>
struct Conv
{
/* static? */ Dest convert(const Src& src) const { throw std::runtime_error("type cast not supported"); }
};
template<> struct Conv<int, bool>
{
bool convert(const int &src) const { return src > 0; }
};
class IStoredVariant
{};
template<typename T>
struct variant_storage : public IStoredVariant
{
variant_storage(const T& value) : m_value(value)
{}
T& getValue(void) { return this->m_value; }
const T& getValue(void) const { return this->m_value; }
template<typename U>
U make_conversion(void) const // just an idea...
{
return Conv<U, T>().convert(this->getValue());
}
protected:
T m_value;
};
class Variant
{
public:
template<typename T>
Variant(const T& value) : m_storage(new variant_storage<T>(value))
{}
IStoredVariant& getImpl(void) { return *this->m_storage; }
const IStoredVariant& getImpl(void) const { return *this->m_storage; }
std::auto_ptr<IStoredVariant> m_storage;
template<typename T>
T get(void) const
{
const IStoredVariant &var = this->getImpl();
// ????????????
// How to perform conversion?
}
template<typename T>
void set(const T &value)
{
this->m_storage.reset(new variant_storage<T>(value));
}
};
int main(void)
{
Variant v(int(23));
bool i = v.get<bool>();
}
From the get<> template method, I only have access to an IStoredVariant pointer, but I need to know the concrete type to choose the Converter<>. Is there any design pattern or workaround to solve this problem?
This is impossible. You would need to have support for templates in virtual functions to make this happen.
In the calling context, you only have the type to be converted to, and you can't retrieve the stored type. In the called context, you only have the stored type and can't retrieve the type to be converted to.
There is no way to pass the type between them, so you can never know both types at once and therefore cannot perform any conversion.
Your problem is intractable.
If you have lost type information, then you cannot recover it (not fully) because the language itself does not support it (no reflexion/introspection).
You can still know the exact type, but you cannot get properties such as conversions to arbitrary types, because conversions mechanisms are baked in at compile-time (depending on constructors, conversion operators and language rules).
If you have only a small subset of types that you are interested in, then Boost.Variant is your best bet.
If you really wanted to have a fully dynamic language... then either ditch C++ or reimplement a dynamic language on top of C++...
You could use the typeid operator to get the type information of the type stored in the variant and compare this to the typeid of the T in get:
Extend the IStoredVariant with this interface definition:
class IStoredVariant
{
...
type_info getTypeId() = 0; // note the abstract definition
...
}
Add the implementation to the concrete variant storage:
template<typename T>
struct variant_storage : public IStoredVariant
{
...
type_info getTypeId() { return typeid(T); }
...
}
Use it in the Variant class:
class Variant
{
...
template<typename T>
T get(void) const
{
const IStoredVariant *var = this->getImpl();
if(typeid(T) == var->getTypeId())
{
// type matches: cast to the type
variant_storage<T>* my_typed_var = static_cast<variant_storage<T>* >(var);
// do something with it
}
}
}
EDIT: You can also look at the property implementation of OGRE which does not use typeid but enums for a specific set of types. All other types are therefore unsupported.

visitor template for boost::variant

I would like to use a boost.variant<T0,T1,T2> as a parameter to a template 'Visitor' class which would provide visitor operators as required by the boost.variant visitor mechanism, in this case all returning void i.e.,
void operator()(T0 value);
void operator()(T1 value);
void operator()(T2 value);
The template would also have for each of the types T0... in the variant a corresponding virtual function which by default does nothing. The user is able inherit from the template class and redefine only those virtual functions which he is interested in. This is something akin to the well-known 'Template Method' pattern.
The only solution I have been able to come up with is by wrapping both the boost::variant and the associated visitor in a single template, and accessing them via typedefs. This works okay, however it feels a little clunky. Here's the code:
#include "boost/variant.hpp"
//create specializations of VariantWrapper for different numbers of variants -
//just show a template for a variant with three types here.
//variadic template parameter list would be even better!
template<typename T0, typename T1, typename T2>
struct VariantWrapper
{
//the type for the variant
typedef boost::variant<T0,T1,T2> VariantType;
//The visitor class for this variant
struct Visitor : public boost::static_visitor<>
{
void operator()(T0 value)
{
Process(value);
}
void operator()(T1 value)
{
Process(value);
}
void operator()(T2 value)
{
Process(value);
}
virtual void Process(T0 val){/*do nothing */}
virtual void Process(T1 val){/*do nothing */}
virtual void Process(T2 val){/*do nothing */}
protected:
Visitor(){}
};
typedef Visitor VisitorType;
private:
VariantWrapper(){}
};
The class is then used as follows:
typedef VariantWapper<bool,int,double> VariantWrapperType;
typedef VariantWrapperType::VariantType VariantType;
typedef VariantWrapperType::VisitorType VisitorType;
struct Visitor : public VisitorType
{
void Process(bool val){/*do something*/}
void Process(int val){/*do something*/}
/* this class is not interested in the double value */
};
VariantType data(true);
apply_visitor(Visitor(),data);
As I say, this seems to work okay but I would prefer it if I didn't have to create a special wrapper class to tie the variant and the visitor together. I would prefer to be able just to use a boost.variant directly to instantiate the template visitor class. I've had a look at using type parameters, non-type parameters and template template parameters but nothing seems to suggest itself. Is what I am trying to do not possible? I may be missing something, and would appreciate it if anyone has any input on this.
The code with Boost Variant and virtual dispatching is a little fishy. Especially taking into account that you know what are you interested in processing during the compile-time and there is absolutely no need in creating a virtual table at run-time in order to achieve your goals.
I would recommend you use partial template specialization. Thus, have a default template method that can accept any type in the variant and will do nothing. For those types you are interested in, just specialize template.
Here is an example. We have three types - Foo, Bar and War. We are interested only in the last two types and have a specialization for them. So Foo is being ignored.
#include <iostream>
#include <boost/variant.hpp>
using namespace std;
using namespace boost;
struct Foo {};
struct Bar {};
struct War {};
typedef variant<Foo, Bar, War> Guess;
struct Guesstimator : public boost::static_visitor<void>
{
template <typename T>
void operator () (T) const
{
}
};
template <>
inline void
Guesstimator::operator () <Bar> (Bar) const
{
cout << "Let's go to a pub!" << endl;
}
template <>
inline void
Guesstimator::operator () <War> (War) const
{
cout << "Make love, not war!" << endl;
}
Here is a simple example of the usage:
int
main ()
{
Guess monday;
apply_visitor (Guesstimator (), monday);
War war;
Guess ww2 (war);
apply_visitor (Guesstimator (), ww2);
Bar irishPub;
Guess friday (irishPub);
apply_visitor (Guesstimator (), friday);
}
The output of this program will be:
Make love, not war!
Let's go to a pub!
Here is another solution. We create a default visitor ignoring everything, except what you have specified in a type list. It is not that convenient because you have to specify a list of types twice - once in a type list and then in each processing method (operator). Plus, the generic template, in fact, will be inheriting your visitor. But nevertheless, here we go:
#include <cstddef>
#include <iostream>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/enable_if.hpp>
// Generic visitor that does magical dispatching of
// types and delegates passes down to your visitor only
// those types specified in a type list.
template <typename Visitor, typename TypeList>
struct picky_visitor :
public boost::static_visitor<void>,
public Visitor
{
template <typename T>
inline void
operator () (T v, typename boost::enable_if< typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
{
Visitor::operator () (v);
}
template <typename T>
inline void
operator () (T v, typename boost::disable_if<typename boost::mpl::contains< TypeList, T >::type >::type *dummy = NULL) const
{
}
};
// Usage example:
struct nil {};
typedef boost::variant<nil, char, int, double> sql_field;
struct example_visitor
{
typedef picky_visitor< example_visitor, boost::mpl::vector<char, int> > value_type;
inline void operator () (char v) const
{
std::cout << "character detected" << std::endl;
}
inline void operator () (int v) const
{
std::cout << "integer detected" << std::endl;
}
};
int
main ()
{
example_visitor::value_type visitor;
sql_field nilField;
sql_field charField ('X');
sql_field intField (1986);
sql_field doubleField (19.86);
boost::apply_visitor (visitor, nilField);
boost::apply_visitor (visitor, charField);
boost::apply_visitor (visitor, intField);
boost::apply_visitor (visitor, doubleField);
}
As time passes new and interesting libraries develop. This question is old, but since then there is a solution that to me personally is far more superior to the ones that have been given so far.
The excellent Mach7 library which allows unprecedented matching (and therefore visiting) capabilities. It is written by Yuriy Solodkyy, Gabriel Dos Reis and Bjarne Stroustrup himself. For the ones stumbling on this question, here is an example taken from the README:
void print(const boost::variant<double,float,int>& v)
{
var<double> d; var<float> f; var<int> n;
Match(v)
{
Case(C<double>(d)) cout << "double " << d << endl; break;
Case(C<float> (f)) cout << "float " << f << endl; break;
Case(C<int> (n)) cout << "int " << n << endl; break;
}
EndMatch
}
I am working with it now and so far it is a real pleasure to use.
Tom, I believe that your question makes much sense in a particular context. Say that you want to store visitors of multiple types in a vector, but you can't because they are all of different types. You have a few choices: use variant again to store visitors, use boost.any, or use virtual functions. I think that virtual functions are an elegant solution here, but certainly not the only one.
Here is how it goes.
First, let's use some variant; bool, int, and float will do.
typedef boost::variant<bool, int, float> variant_type;
Then comes the base class, more or less as you had it.
template
struct Visitor : public boost::static_visitor<>
{
void operator()(T0 value)
{
Process(value);
}
void operator()(T1 value)
{
Process(value);
}
void operator()(T2 value)
{
Process(value);
}
virtual void Process(T0 val){ std::cout << "I am Visitor at T0" << std::endl; }
virtual void Process(T1 val){ std::cout << "I am Visitor at T1" << std::endl; }
virtual void Process(T2 val){ std::cout << "I am Visitor at T2" << std::endl; }
protected:
Visitor(){}
};
Next, we have two specific variants.
template
struct Visitor1 : public Visitor
{
void Process(T0 val){ std::cout << "I am Visitor1 at T0" << std::endl; }
void Process(T2 val){ std::cout << "I am Visitor1 at T2" << std::endl; }
};
template
struct Visitor2 : public Visitor
{
void Process(T1 val){ std::cout << "I am Visitor2 at T1" << std::endl; }
void Process(T2 val){ std::cout << "I am Visitor2 at T2" << std::endl; }
};
Finally, we can make a single vector of different variants:
int main() {
variant_type data(1.0f);
std::vector*> v;
v.push_back(new Visitor1());
v.push_back(new Visitor2());
apply_visitor(*v[0],data);
apply_visitor(*v[1],data);
data = true;
apply_visitor(*v[0],data);
apply_visitor(*v[1],data);
return 0;
}
And here is the output:
I am Visitor1 at T2
I am Visitor2 at T2
I am Visitor1 at T0
I am Visitor at T0
If for some reason I needed to have different variants in one container, I would surely consider this solution. I would also think how much worse/better would it be to actually stick the visitors into another variant. The nice thing about using inheritance is that it is extensible post factum: you can always inherit from a class, but once a variant is set, you can't change it without actually touching the existing code.

Template metaprogram converting type to unique number

I just started playing with metaprogramming and I am working on different tasks just to explore the domain. One of these was to generate a unique integer and map it to type, like below:
int myInt = TypeInt<AClass>::value;
Where value should be a compile time constant, which in turn may be used further in meta programs.
I want to know if this is at all possible, and in that case how. Because although I have learned much about exploring this subject I still have failed to come up with an answer.
(P.S. A yes/no answer is much more gratifying than a c++ solution that doesn't use metaprogramming, as this is the domain that I am exploring)
In principle, this is possible, although the solution probably isn't what you're looking for.
In short, you need to provide an explicit mapping from the types to the integer values, with one entry for each possible type:
template< typename T >
struct type2int
{
// enum { result = 0 }; // do this if you want a fallback value
};
template<> struct type2int<AClass> { enum { result = 1 }; };
template<> struct type2int<BClass> { enum { result = 2 }; };
template<> struct type2int<CClass> { enum { result = 3 }; };
const int i = type2int<T>::result;
If you don't supply the fallback implementation in the base template, this will fail for unknown types if T, otherwise it would return the fallback value.
Depending on your context, there might be other possibilities, too. For example, you could define those numbers within within the types themselves:
class AClass {
public:
enum { inta_val = 1 };
// ...
};
class BClass {
public:
enum { inta_val = 2 };
// ...
};
// ...
template< typename T >
struct type2int
{
enum { result = T::int_val }; // will fail for types without int_val
};
If you give more context, there might be other solutions, too.
Edit:
Actually there isn't any more context to it. I was looking into if it actually was possible, but without assigning the numbers itself.
I think Mike's idea of ordering is a good way to do this (again, for a fixed set of types) without having to explicitly assign numbers: they're implicitly given by the ordering. However, I think that this would be easier by using a type list. The index of any type in the list would be its number. I think something like the following might do:
// basic type list manipulation stuff
template< typename T1, typename T2, typename T3...>
struct type_list;
// meta function, List is assumed to be some instance of type_list
template< typename T, class List >
struct index_of {
enum { result = /* find index of T in List */ };
};
// the list of types you support
typedef type_list<AClass, BClass, CClass> the_type_list;
// your meta function
template< typename T >
struct type2int
{
enum { result = index_of<T, the_type_list>::result };
};
This does what you want. Values are assigned on need. It takes advantage of the way statics in functions are assigned.
inline size_t next_value()
{
static size_t id = 0;
size_t result = id;
++id;
return result;
}
/** Returns a small value which identifies the type.
Multiple calls with the same type return the same value. */
template <typename T>
size_t get_unique_int()
{
static size_t id = next_value();
return id;
}
It's not template metaprogramming on steroids but I count that as a good thing (believe me!)
Similiar to Michael Anderson's approach but this implementation is fully standards compliant and can be performed at compile time. Beginning with C++17 it looks like constexpr values will be allowed to be used as a template parameter for other template meta programming purposes. Also unique_id_type can be compared with ==, !=, >, <, etc. for sorting purposes.
// the type used to uniquely identify a list of template types
typedef void (*unique_id_type)();
// each instantiation of this template has its own static dummy function. The
// address of this function is used to uniquely identify the list of types
template <typename... Arguments>
struct IdGen {
static constexpr inline unique_id_type get_unique_id()
{
return &IdGen::dummy;
}
private:
static void dummy(){};
};
The closest I've come so far is being able to keep a list of types while tracking the distance back to the base (giving a unique value). Note the "position" here will be unique to your type if you track things correctly (see the main for the example)
template <class Prev, class This>
class TypeList
{
public:
enum
{
position = (Prev::position) + 1,
};
};
template <>
class TypeList<void, void>
{
public:
enum
{
position = 0,
};
};
#include <iostream>
int main()
{
typedef TypeList< void, void> base; // base
typedef TypeList< base, double> t2; // position is unique id for double
typedef TypeList< t2, char > t3; // position is unique id for char
std::cout << "T1 Posn: " << base::position << std::endl;
std::cout << "T2 Posn: " << t2::position << std::endl;
std::cout << "T3 Posn: " << t3::position << std::endl;
}
This works, but naturally I'd like to not have to specify a "prev" type somehow. Preferably figuring out a way to track this automatically. Maybe I'll play with it some more to see if it's possible. Definitely an interesting/fun puzzle.
I think it is possible to do it for a fixed set of types, but quite a bit of work. You'll need to define a specialisation for each type, but it should be possible to use compile-time asserts to check for uniqueness. I'll assume a STATIC_ASSERT(const_expr), like the one in Boost.StaticAssert, that causes a compilation failure if the expression is false.
Suppose we have a set of types that we want unique IDs for - just 3 for this example:
class TypeA;
class TypeB;
typedef int TypeC;
We'll want a way to compare types:
template <typename T, typename U> struct SameType
{
const bool value = false;
};
template <typename T> struct SameType<T,T>
{
const bool value = true;
};
Now, we define an ordering of all the types we want to enumerate:
template <typename T> struct Ordering {};
template <> struct Ordering<void>
{
typedef TypeC prev;
typedef TypeA next;
};
template <> struct Ordering<TypeA>
{
typedef void prev;
typedef TypeB next;
};
template <> struct Ordering<TypeB>
{
typedef TypeA prev;
typedef TypeC next;
};
template <> struct Ordering<TypeC>
{
typedef TypeB prev;
typedef void next;
};
Now we can define the unique ID:
template <typename T> struct TypeInt
{
STATIC_ASSERT(SameType<Ordering<T>::prev::next, T>::value);
static int value = TypeInt<T>::prev::value + 1;
};
template <> struct TypeInt<void>
{
static int value = 0;
};
NOTE: I haven't tried compiling any of this. It may need typename adding in a few places, and it may not work at all.
You can't hope to map all possible types to an integer field, because there are an unbounded number of them: pointer types with arbitrary levels of indirection, array types of arbitrary size and rank, function types with arbitrary numbers of arguments, and so on.
I'm not aware of a way to map a compile-time constant integer to a type, but I can give you the next best thing. This example demonstrates a way to generate a unique identifier for a type which - while it is not an integral constant expression - will generally be evaluated at compile time. It's also potentially useful if you need a mapping between a type and a unique non-type template argument.
struct Dummy
{
};
template<typename>
struct TypeDummy
{
static const Dummy value;
};
template<typename T>
const Dummy TypeDummy<T>::value = Dummy();
typedef const Dummy* TypeId;
template<typename T, TypeId p = &TypeDummy<T>::value>
struct TypePtr
{
static const TypeId value;
};
template<typename T, TypeId p>
const TypeId TypePtr<T, p>::value = p;
struct A{};
struct B{};
const TypeId typeA = TypePtr<A>::value;
const TypeId typeB = TypePtr<B>::value;
I developed this as a workaround for performance issues with ordering types using typeid(A) == typeid(B), which a certain compiler fails to evaluate at compile time. It's also useful to be able to store TypeId values for comparison at runtime: e.g. someType == TypePtr<A>::value
This may be doing some "bad things" and probably violates the standard in some subtle ways... but thought I'd share anyway .. maybe some one else can sanitise it into something 100% legal? But it seems to work on my compiler.
The logic is this .. construct a static member function for each type you're interested in and take its address. Then convert that address to an int. The bits that are a bit suspect are : 1) the function ptr to int conversion. and 2) I'm not sure the standard guarantees that the addresses of the static member functions will all correctly merge for uses in different compilation units.
typedef void(*fnptr)(void);
union converter
{
fnptr f;
int i;
};
template<typename T>
struct TypeInt
{
static void dummy() {}
static int value() { converter c; c.f = dummy; return c.i; }
};
int main()
{
std::cout<< TypeInt<int>::value() << std::endl;
std::cout<< TypeInt<unsigned int>::value() << std::endl;
std::cout<< TypeInt< TypeVoidP<int> >::value() << std::endl;
}
I don't think it's possible without assigning the numbers yourself or having a single file know about all the types. And even then you will run into trouble with template classes. Do you have to assign the number for each possible instantiation of the class?
type2int as compile time constant is impossible even in C++11. Maybe some rich guy should promise a reward for the anwser? Until then I'm using the following solution, which is basically equal to Matthew Herrmann's:
class type2intbase {
template <typename T>
friend struct type2int;
static const int next() {
static int id = 0; return id++;
}
};
template <typename T>
struct type2int {
static const int value() {
static const int id = type2intbase::next(); return id;
}
};
Note also
template <typename T>
struct type2ptr {
static const void* const value() {
return typeid(T).name();
}
};