From http://llvm.org/docs/CodingStandards.html#ci_rtti_exceptions
LLVM does make extensive use of a
hand-rolled form of RTTI that use
templates like isa<>, cast<>, and
dyn_cast<>. This form of RTTI is
opt-in and can be added to any class.
It is also substantially more
efficient than dynamic_cast<>.
How is isa and the others implemented?
First of all, the LLVM system is extremely specific and not at all a drop-in replacement for the RTTI system.
Premises
For most classes, it is unnecessary to generate RTTI information
When it is required, the information only makes sense within a given hierarchy
We preclude multi-inheritance from this system
Identifying an object class
Take a simple hierarchy, for example:
struct Base {}; /* abstract */
struct DerivedLeft: Base {}; /* abstract */
struct DerivedRight:Base {};
struct MostDerivedL1: DerivedLeft {};
struct MostDerivedL2: DerivedLeft {};
struct MostDerivedR: DerivedRight {};
We will create an enum specific to this hierarchy, with an enum member for each of the hierarchy member that can be instantiated (the others would be useless).
enum BaseId {
DerivedRightId,
MostDerivedL1Id,
MostDerivedL2Id,
MostDerivedRId
};
Then, the Base class will be augmented with a method that will return this enum.
struct Base {
static inline bool classof(Base const*) { return true; }
Base(BaseId id): Id(id) {}
BaseId getValueID() const { return Id; }
BaseId Id;
};
And each concrete class is augmented too, in this manner:
struct DerivedRight: Base {
static inline bool classof(DerivedRight const*) { return true; }
static inline bool classof(Base const* B) {
switch(B->getValueID()) {
case DerivedRightId: case MostDerivedRId: return true;
default: return false;
}
}
DerivedRight(BaseId id = DerivedRightId): Base(id) {}
};
Now, it is possible, simply, to query the exact type, for casting.
Hiding implementation details
Having the users murking with getValueID would be troublesome though, so in LLVM this is hidden with the use of classof methods.
A given class should implement two classof methods: one for its deepest base (with a test of the suitable values of BaseId) and one for itself (pure optimization). For example:
struct MostDerivedL1: DerivedLeft {
static inline bool classof(MostDerivedL1 const*) { return true; }
static inline bool classof(Base const* B) {
return B->getValueID() == MostDerivedL1Id;
}
MostDerivedL1(): DerivedLeft(MostDerivedL1Id) {}
};
This way, we can check whether a cast is possible or not through the templates:
template <typename To, typename From>
bool isa(From const& f) {
return To::classof(&f);
}
Imagine for a moment that To is MostDerivedL1:
if From is MostDerivedL1, then we invoke the first overload of classof, and it works
if From is anything other, then we invoke the second overload of classof, and the check uses the enum to determine if the concrete type match.
Hope it's clearer.
Just adding stuff to osgx's answer: basically each class should implement classof() method which does all the necessary stuff. For example, the Value's classof() routine looks like this:
// Methods for support type inquiry through isa, cast, and dyn_cast:
static inline bool classof(const Value *) {
return true; // Values are always values.
}
To check whether we have a class of the appropriate type, each class has it's unique ValueID. You can check the full list of ValueID's inside the include/llvm/Value.h file. This ValueID is used as follows (excerpt from Function.h):
/// Methods for support type inquiry through isa, cast, and dyn_cast:
static inline bool classof(const Function *) { return true; }
static inline bool classof(const Value *V) {
return V->getValueID() == Value::FunctionVal;
}
So, in short: every class should implement classof() method which performs the necessary decision. The implementation in question consists of the set of unique ValueIDs. Thus in order to implement classof() one should just compare the ValueID of the argument with own ValueID.
If I remember correctly, the first implementation of isa<> and friends were adopted from boost ~10 years ago. Right now the implementations diverge significantly :)
I should mention that http://llvm.org/docs/ProgrammersManual.html#isa - this document have some additional description.
The source code of isa, cast and dyn_cast is located in single file, and commented a lot.
http://llvm.org/doxygen/Casting_8h_source.html
00047 // isa<X> - Return true if the parameter to the template is an instance of the
00048 // template type argument. Used like this:
00049 //
00050 // if (isa<Type*>(myVal)) { ... }
00051 //
00052 template <typename To, typename From>
00053 struct isa_impl {
00054 static inline bool doit(const From &Val) {
00055 return To::classof(&Val);
00056 }
00057 };
00193 // cast<X> - Return the argument parameter cast to the specified type. This
00194 // casting operator asserts that the type is correct, so it does not return null
00195 // on failure. It does not allow a null argument (use cast_or_null for that).
00196 // It is typically used like this:
00197 //
00198 // cast<Instruction>(myVal)->getParent()
00199 //
00200 template <class X, class Y>
00201 inline typename cast_retty<X, Y>::ret_type cast(const Y &Val) {
00202 assert(isa<X>(Val) && "cast<Ty>() argument of incompatible type!");
00203 return cast_convert_val<X, Y,
00204 typename simplify_type<Y>::SimpleType>::doit(Val);
00205 }
00218 // dyn_cast<X> - Return the argument parameter cast to the specified type. This
00219 // casting operator returns null if the argument is of the wrong type, so it can
00220 // be used to test for a type as well as cast if successful. This should be
00221 // used in the context of an if statement like this:
00222 //
00223 // if (const Instruction *I = dyn_cast<Instruction>(myVal)) { ... }
00224 //
00225
00226 template <class X, class Y>
00227 inline typename cast_retty<X, Y>::ret_type dyn_cast(const Y &Val) {
00228 return isa<X>(Val) ? cast<X, Y>(Val) : 0;
00229 }
Related
Having returned relatively recently to C++ after decades of Java, I am currently struggling with a template-based approach to data conversion for instances where type erasure has been applied. Please bear with me, my nomenclature may still be off for C++-natives.
This is what I am trying to achieve:
Implement dynamic variables which are able to hold essentially any value type
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to hold variable instances in containers, independent of their value type
Convert between variable value and representation using conversion functions
Be able to introduce new representations just by providing new conversion functions
Constraints: use only C++-11 features if possible, no use of libraries like boost::any etc.
A rough sketch of this might look like this:
#include <iostream>
#include <vector>
void convert(const std::string &f, std::string &t) { t = f; }
void convert(const int &f, std::string &t) { t = std::to_string(f); }
void convert(const std::string &f, int &t) { t = std::stoi(f); }
void convert(const int &f, int &t) { t = f; }
struct Variable {
virtual void get(int &i) = 0;
virtual void get(std::string &s) = 0;
};
template <typename T> struct VariableImpl : Variable {
T value;
VariableImpl(const T &v) : value{v} {};
void get(int &i) { convert(value, i); };
void get(std::string &s) { convert(value, s); };
};
int main() {
VariableImpl<int> v1{42};
VariableImpl<std::string> v2{"1234"};
std::vector<Variable *> vars{&v1, &v2};
for (auto &v : vars) {
int i;
v->get(i);
std::string s;
v->get(s);
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
return 0;
}
The code does what it is supposed to do, but obvoiusly I would like to get rid of Variable::get(int/std::string/...) and instead template them, because otherwise every new representation requires a definition and an implementation with the latter being exactly the same as all the others.
I've played with various approaches so far, like virtual templated, methods, applying the CRDT with intermediate type, various forms of wrappers, yet in all of them I get bitten by the erased value type of VariableImpl. On one hand, I think there might not be a solution, because after type erasure, the compiler cannot possibly know what templated getters and converter calls it must generate. On the other hand I think i might be missing something really essential here and there should be a solution despite the constraints mentioned above.
This is a classical double dispatch problem. The usual solution to this problem is to have some kind of dispatcher class with multiple implementations of the function you want to dispatch (get in your case). This is called the visitor pattern. The well-known drawback of it is the dependency cycle it creates (each class in the hierarchy depends on all other classes in the hierarchy). Thus there's a need to revisit it each time a new type is added. No amount of template wizardry eliminates it.
You don't have a specialised Visitor class, your Variable serves as a Visitor of itself, but this is a minor detail.
Since you don't like this solution, there is another one. It uses a registry of functions populated at run time and keyed on type identification of their arguments. This is sometimes called "Acyclic Visitor".
Here's a half-baked C++11-friendly implementation for your case.
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
struct Variable
{
virtual void convertValue(Variable& to) const = 0;
virtual ~Variable() {};
virtual std::type_index getTypeIdx() const = 0;
template <typename K> K get() const;
static std::map<std::pair<std::type_index, std::type_index>,
std::function<void(const Variable&, Variable&)>>
conversionMap;
template <typename T, typename K>
static void registerConversion(K (*fn)(const T&));
};
template <typename T>
struct VariableImpl : Variable
{
T value;
VariableImpl(const T &v) : value{v} {};
VariableImpl() : value{} {}; // this is needed for a declaration of
// `VariableImpl<K> below
// It can be avoided but it is
// a story for another day
void convertValue(Variable& to) const override
{
auto typeIdxFrom = getTypeIdx();
auto typeIdxTo = to.getTypeIdx();
if (typeIdxFrom == typeIdxTo) // no conversion needed
{
dynamic_cast<VariableImpl<T>&>(to).value = value;
}
else
{
auto fcnIter = conversionMap.find({getTypeIdx(), to.getTypeIdx()});
if (fcnIter != conversionMap.end())
{
fcnIter->second(*this, to);
}
else
throw std::logic_error("no conversion");
}
}
std::type_index getTypeIdx() const override
{
return std::type_index(typeid(T));
}
};
template <typename K> K Variable::get() const
{
VariableImpl<K> vk;
convertValue(vk);
return vk.value;
}
template <typename T, typename K>
void Variable::registerConversion(K (*fn)(const T&))
{
// add a mutex if you ever spread this over multiple threads
conversionMap[{std::type_index(typeid(T)), std::type_index(typeid(K))}] =
[fn](const Variable& from, Variable& to) {
dynamic_cast<VariableImpl<K>&>(to).value =
fn(dynamic_cast<const VariableImpl<T>&>(from).value);
};
}
Now of course you need to call registerConversion e.g. at the beginning of main and pass it each conversion function.
Variable::registerConversion(int_to_string);
Variable::registerConversion(string_to_int);
This is not ideal, but hardly anything is ever ideal.
Having said all that, I would recommend you revisit your design. Do you really need all these conversions? Why not pick one representation and stick with it?
Implement dynamic variables which are able to hold essentially any value type
Be able to hold variable instances in containers, independent of their value type
These two requirements are quite challenging on its own. The class templates don't really encourage inheritance, and you already did the right thing to hold what you asked for: introduced a common base class for the class template, which you can later refer to in order to store pointers of the said type in a collection.
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to introduce new representations just by providing new conversion functions
This is where it breaks. Function templates assume common implementation for different types, while inheritance assumes different implementation for the same types.
You goal is to introduce different implementation for different types, and in order to make your requirements viable you have to switch to one of those two options instead (or put up with a number of functions for each case which you have already introduced yourself)
Edit:
One of the strategies you may employ to enforce inheritance approach is generalisation of the arguments to the extent where they can be used interchangeably by the abstract interface. E.g. you may wrap the converting arguments inside of a union like this:
struct Variable {
struct converter_type {
enum { INT, STRING } type;
union {
int* m_int;
std::string* m_string;
};
};
virtual void get(converter_type& var) = 0;
virtual ~Variable() = default;
};
And then take whatever part of it inside of the implementation:
void get(converter_type& var) override {
switch (var.type) {
case converter_type::INT:
convert(value, var.m_int);
break;
case converter_type::STRING:
convert(value, var.m_string);
break;
}
}
To be honest I don't think this is a less verbose approach compared to just having a number of functions for each type combination, but i think you got the idea that you can just wrap your arguments somehow to cement the abstract class interface.
Implement std::any. It is similar to boost::any.
Create a conversion dispatcher based off typeids. Store your any alongside the conversion dispatcher.
"new conversion functions" have to be passed to the dispatcher.
When asked to convert to a type, pass that typeid to the dispatcher.
So we start with these 3 types:
using any = std::any; // implement this
using converter = std::function<any(any const&)>;
using convert_table = std::map<std::type_index, converter>;
using convert_lookup = convert_table(*)();
template<class T>
convert_table& lookup_convert_table() {
static convert_table t;
return t;
}
struct converter_any: any {
template<class T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, converter_any>::value, bool
>::type = true
>
converter_any( T&& t ):
any(std::forward<T>(t)),
table(&lookup_convert_table<typename std::decay<T>::type>())
{}
converter_any(converter_any const&)=default;
converter_any(converter_any &&)=default;
converter_any& operator=(converter_any const&)=default;
converter_any& operator=(converter_any&&)=default;
~converter_any()=default;
converter_any()=default;
convert_table const* table = nullptr;
template<class U>
U convert_to() const {
if (!table)
throw 1; // make a better exception than int
auto it = table->find(typeid(U));
if (it == table->end())
throw 2; // make a better exception than int
any const& self = *this;
return any_cast<U>((it->second)(self));
}
};
template<class Dest, class Src>
bool add_converter_to_table( Dest(*f)(Src const&) ) {
lookup_convert_table<Src>()[typeid(Dest)] = [f](any const& s)->any {
Src src = std::any_cast<Src>(s);
auto r = f(src);
return r;
};
return true;
}
now your code looks like:
const bool bStringRegistered =
add_converter_to_table(+[](std::string const& f)->std::string{ return f; })
&& add_converter_to_table(+[](std::string const& f)->int{ return std::stoi(f); });
const bool bIntRegistered =
add_converter_to_table(+[](int const& i)->int{ return i; })
&& add_converter_to_table(+[](int const& i)->std::string{ return std::to_string(i); });
int main() {
converter_any v1{42};
converter_any v2{std::string("1234")};
std::vector<converter_any> vars{v1, v2}; // copies!
for (auto &v : vars) {
int i = v.convert_to<int>();
std::string s = v.convert_to<std::string>();
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
}
live example.
...
Ok, what did I do?
I used any to be a smart void* that can store anything. Rewriting this is a bad idea, use someone else's implementation.
Then, I augmented it with a manually written virtual function table. Which table I add is determined by the constructor of my converter_any; here, I know the type stored, so I can store the right table.
Typically when using this technique, I'd know what functions are in there. For your implementation we do not; so the table is a map from the type id of the destination, to a conversion function.
The conversion function takes anys and returns anys -- again, don't repeat this work. And now it has a fixed signature.
To add support for a type, you independently register conversion functions. Here, my conversion function registration helper deduces the from type (to determine which table to register it in) and the destination type (to determine which entry in the table), and then automatically writes the any boxing/unboxing code for you.
...
At a higher level, what I'm doing is writing my own type erasure and object model. C++ has enough power that you can write your own object models, and when you want features that the default object model doesn't solve, well, roll a new object model.
Second, I'm using value types. A Java programmer isn't used to value types having polymorphic behavior, but much of C++ works much better if you write your code using value types.
So my converter_any is a polymorphic value type. You can store copies of them in vectors etc, and it just works.
I have some code that accepts one type of object and creates another type of object based on the type of the first. (There is a 1->1 relationship between the types.) I originally used a hash table (unordered_map<>) with a key based on the type of the first object to associate a creation function for the second object. But as I am learning more about the C++ features introduced since the last time I was full-time C++, I discovered std::variant<>.
I have successfully converted the implementation to use this C++17 feature. However, there is one remaining piece that is still a bit cumbersome. The design makes a call to a static member function of the second class to validate the contents of the first object, before instantiating an object of the second class. To handle this right now, I'm using a visitor structure with function operators overloaded for each input type.
What I'm wondering is if there is some way to use a template for the association, rather than the copied code with only the types different?
I've tried looking at the way std::variant<> works, and I see where the index of the type can be obtained with .index(). I can see how to instantiate an object based on an index, which I might use if I created a second std::variant<> with the object types. But, as you can see, I don't want to instantiate the object until the parameters have been validated. The function that does that is static, and I don't see a way to associate the parms type with the object type in a way that lets me make the static call.
(I also realize that these two visitor structures can be combined in the code below, but in the real code, the creation is longer and more complicated, and I would rather not have copies of it in each overload.)
struct Type1Parms {};
struct Type2Parms {};
struct Type3Parms {};
...
struct TypeBase {};
struct Type1 : public TypeBase
{
static bool ValidateParms(const Type1Parms&);
Type1(const Type1Parms&);
};
struct Type2 : public TypeBase
{
static bool ValidateParms(const Type2Parms&);
Type2(const Type2Parms&);
};
struct Type3 : public TypeBase
{
static bool ValidateParms(const Type3Parms&);
Type3(const Type3Parms&);
};
...
struct ValidateParmsVisitor
{
bool operator()(const Type1Parms& parms)
{
return Type1::ValidateParms(parms);
}
bool operator()(const Type2Parms& parms)
{
return Type2::ValidateParms(parms);
}
bool operator()(const Type3Parms& parms)
{
return Type3::ValidateParms(parms);
}
...
};
using TypeParms = std::variant<Type1Parms, Type2Parms, Type3Parms, ...>;
struct CreateObjectVisitor
{
std::unique_ptr<TypeBase> operator()(const Type1Parms& parms)
{
return std::make_unique<Type1>(parms);
}
std::unique_ptr<TypeBase> operator()(const Type2Parms& parms)
{
return std::make_unique<Type2>(parms);
}
std::unique_ptr<TypeBase> operator()(const Type3Parms& parms)
{
return std::make_unique<Type3>(parms);
}
...
};
template<typename TParms>
std::unique_ptr<TypeBase> CreateType(const TParms& parms)
{
unique_ptr<TypeBase> obj;
if (visit(ValidateParmsVisitor{}, parms))
obj = visit(CreateObjectVisitor{}, parms);
return std::move(obj);
}
Is there a way to make this association, especially as a type that can be used with a static member function call?
EDIT: I should explain that this is part of a much larger project, with a number of other design criteria that shape its design.
For example, this is for a client interface, where the API is meant to be as simple as can be expressed. The client only has visibility (via header) to the parms structures and a function that takes the parms & returns an object that contains the objects mentioned above. The original design did indeed have a base structure for the parms, which obviously had to be in the public header. However, this meant that a client could inherit from the base class themselves and pass this into the object creation function, or inherit from the acceptable structures. To avoid segfaults, this necessitated adding runtime checks to be sure the types were acceptable, which was mostly handled by the hash design--although it wasn't quite that simple. When I removed the hash design, I also lost this method of type validation, but I recognized that this would be replaced by a compile time check with the variant<>, handling custom structures (no base to check now). I also learned about the C++ version of the final keyword which handled the inheritance issue.
Additionally, while the code above does not show it, the parms structures contain multiple members and the ValidateParms() functions actually attempt to validate whether the values and combinations are valid.
You can create traits for the association:
template <typename T> struct from_param;
template <> struct from_param<Type1Parms> { using type = Type1; };
template <> struct from_param<Type2Parms> { using type = Type2; };
template <> struct from_param<Type3Parms> { using type = Type3; };
Then, you might do
using TypeParms = std::variant<Type1Parms, Type2Parms, Type3Parms>;
std::unique_ptr<TypeBase> CreateType(const TypeParms& parms)
{
if (std::visit([](const auto& param){
return from_param<std::decay_t<decltype(param)>>::type::ValidateParms(parms);
}, parms))
{
return std::visit([](const auto& param) -> std::unique_ptr<TypeBase> {
return std::make_unique<typename from_param<std::decay_t<decltype(param)>>::type>(parms);
}, parms);
}
return nullptr;
}
Demo
or without variant, if you call with correct type:
template <typename T>
auto CreateType(const T& parms)
{
if (from_param<T>::type::ValidateParms(parms))
{
return std::make_unique<typename from_param<T>::type>(parms);
}
return nullptr;
}
There is a very simple method, a set of overloaded functions:
unique_ptr<TypeBase> CreateType(Type1Params const& params)
{
return make_unique<Type1>(params);
}
unique_ptr<TypeBase> CreateType(Type2Params const& params)
{
return make_unique<Type2>(params);
}
unique_ptr<TypeBase> CreateType(Type3Params const& params)
{
return make_unique<Type3>(params);
}
Notes:
You can add another overload to catch other parameters and then return null, but I think a compile-time error would be preferable.
You could also use a template function and specializations, but there's probably little typing to safe that way.
I'm implementing a graph class, with each vertex having a Label of not necessarily the same type. I want the user to be able to provide any Labels (at compile time), without the Graph or the Vertex to know what the type is. For this, I used templated polymorphism, which I've hidden inside a Label class, in order for the Labels to have value semantics. It works like a charm and the relevant code is this (ignore the commented parts for now):
//Label.hpp:
#include <memory>
class Label {
public:
template<class T> Label(const T& name) : m_pName(new Name<T>(name)) {}
Label(const Label& other) : m_pName(other.m_pName->copy()) {}
// Label(const Label& other, size_t extraInfo) : m_pName(other.m_pName->copyAndAddInfo(extraInfo)) {}
bool operator==(const Label& other) const { return *m_pName == *other.m_pName; }
private:
struct NameBase {
public:
virtual ~NameBase() = default;
virtual NameBase* copy() const = 0;
// virtual NameBase* copyAndAddInfo(size_t info) const = 0;
virtual bool operator==(const NameBase& other) const = 0;
};
template<class T> struct Name : NameBase {
public:
Name(T name) : m_name(std::move(name)) {}
NameBase* copy() const override { return new Name<T>(m_name); }
// NameBase* copyAndAddInfo(size_t info) const override {
// return new Name<std::pair<T, size_t>>(std::make_pair(m_name, info));
// }
bool operator==(const NameBase& other) const override {
const auto pOtherCasted = dynamic_cast<const Name<T>*>(&other);
if(pOtherCasted == nullptr) return false;
return m_name == pOtherCasted->m_name;
}
private:
T m_name;
};
std::unique_ptr<NameBase> m_pName;
};
One requirement of the user (aka me) is to be able to create disjoint unions of Graphs (he is already able to create dual Graphs, unions of Graphs (where vertices having the same Label, are mapped to the same vertex), etc.). The wish is that the labels of the new Graph are pairs of the old label and some integer, denoting from which graph the label came (this also ensures that the new labels are all different). For this, I thought that I could use the commented parts of the Label class, but the problem that my g++17 compiler has, is that the moment I define the first Label with some type T, it tries to instantiate everything that could be used:
Name<T>, Name<std::pair<T, size_t>>, Name<std::pair<std::pair<T, size_t>, size_t>>, ...
Try for example to compile this (just an example, that otherwise works):
// testLabel.cpp:
#include "Label.hpp"
#include <vector>
#include <iostream>
int main() {
std::vector<Label> labels;
labels.emplace_back(5);
labels.emplace_back(2.1);
labels.emplace_back(std::make_pair(true, 2));
Label testLabel(std::make_pair(true, 2));
for(const auto& label : labels)
std::cout<<(label == testLabel)<<std::endl;
return 0;
}
The compilation just freezes. (I do not get the message "maximum template recursion capacity exceeded", that I saw others get, but it obviously tries to instantiate everything). I've tried to separate the function in another class and explicitly initialize only the needed templates, in order to trick the compiler, but with no effect.
The desired behaviour (I do not know if possible), is to instantiate the used template classes (together with the member function declarations), but define the member functions lazily, i.e. only if they really get called. For example, if I call Label(3), there should be a class Name<int>, but the function
NameBase* Name<int>::copyAndAddInfo(size_t info) const;
shall only be defined if I call it, at some point. (thus, the Name<std::pair<int, size_t>> is only going to be instantiated on demand)
It feels like something which should be doable, since the compiler already defines templated functions on demand.
An idea whould be to completely change the implementation and use variants, but
I do not want to keep track of the types the user needs manually, and
I quite like this implementation approach and want to see its limits, before changing it.
Does anyone have any hints on how I could solve this problem?
To directly answer your question, the virtual and template combo makes it impossible for the compiler to lazily implement the body copyAndAddInfo. The virtual base type pointer hides the type information, so when the compiler sees other.m_pName->copyAndAddInfo, it couldn't know what type it needs to lazily implement.
EDIT:
Ok, so based on your rationale for using templates, it seems like you only want to accept labels of different types, and might not actually care if the disjoint union information is part of the type. If that's the case, you could move it from the name to the label, and make it run-time information:
class Label {
public:
template<class T> Label(const T& name) : m_pName(new Name<T>(name)) {}
Label(const Label& other) : m_pName(other.m_pName->copy()), m_extraInfo(other.m_extraInfo) { }
Label(const Label& other, size_t extraInfo) : m_pName(other.m_pName->copy()), m_extraInfo(other.m_extraInfo) {
m_extraInfo.push_back(extraInfo);
}
bool operator==(const Label& other) const {
return *m_pName == *other.m_pName && std::equal(
m_extraInfo.begin(), m_extraInfo.end(),
other.m_extraInfo.begin(), other.m_extraInfo.end()); }
private:
struct NameBase { /* same as before */ };
std::vector<size_t> m_extraInfo;
std::unique_ptr<NameBase> m_pName;
};
If the disjoint union info being part of the type is important, than please enjoy my original sarcastic answer below.
ORIGINAL ANSWER:
That said, if you're willing to put a cap on the recursion, I have an evil solution for you that works for up to N levels of nesting: use template tricks to count the level of nesting. Then use SFINAE to throw an error after N levels, instead of recursing forever.
First, to count the levels of nesting:
template <typename T, size_t Level>
struct CountNestedPairsImpl
{
static constexpr size_t value = Level;
};
template <typename T, size_t Level>
struct CountNestedPairsImpl<std::pair<T, size_t>, Level> : CountNestedPairsImpl<T, Level + 1>
{
using CountNestedPairsImpl<T, Level + 1>::value;
};
template <typename T>
using CountNestedPairs = CountNestedPairsImpl<T, 0>;
Then, use std::enable_if<> to generate different bodies based on the nesting level:
constexpr size_t NESTING_LIMIT = 4;
NameBase* copyAndAddInfo(size_t info) const override {
return copyAndAddInfoImpl(info);
}
template <typename U = T, typename std::enable_if<CountNestedPairs<U>::value < NESTING_LIMIT, nullptr_t>::type = nullptr>
NameBase* copyAndAddInfoImpl(size_t info) const {
return new Name<std::pair<T, size_t>>(std::make_pair(m_name, info));
}
template <typename U = T, typename std::enable_if<CountNestedPairs<U>::value >= NESTING_LIMIT, nullptr_t>::type = nullptr>
NameBase* copyAndAddInfoImpl(size_t info) const {
throw std::runtime_error("too much disjoint union nesting");
}
Why did I call this evil? It's going to generate every possible level of nesting allowed, so if you use NESTING_LIMIT=20 it will generate 20 classes per label type. But hey, at least it compiles!
https://godbolt.org/z/eaQTzB
I'm trying to create a statistics system in C++ which will allow me to associate a string with a value of an arbitrary type. Currently, I have it working with an enum that keeps track of the type and a void * that points to the object, but this requires me to make individual if statements for all of the types I want to support. I'd like to have it so that I can support any arbitrary type using some kind of template. I've created some test code that sort of works, but there are issues:
class Test {
std::type_index type;
void *value;
public:
template <typename T>
Test(T val) : type(typeid(val)) {
T *val_p = new T;
*val_p = val;
value = (void *)val;
}
Test() : type(typeid(void)) {
value = nullptr;
}
~Test() {
//no idea how I could make this work
}
template <typename T>
T get() {
if (std::type_index(typeid(T)) == type) {
T *val_p = (T *)value;
return *val_p;
} else {
throw std::bad_typeid();
}
}
};
What I have so far works, but I don't think it would be possible to implement a destructor or copy/move constructors. The whole point is I want to store this all in a single std::unordered_map, so I can't (AFAIK) just make a template class and go from there. So, is it possible to do what I'm trying to do, and if so, how would I do it?
Based on the suggestion of GManNickG, I'm going with boost::any, as it most closely resembles what I'm looking for.
I haven't yet implemented it into the code, but the basic structure will be something along the lines of:
#include <typeinfo>
#include <boost/any.hpp>
class Statistic {
boost::any value;
public:
template <typename T>
Statistic(T val) : value(val) {}
Statistic() : value() {}
template <typename T>
bool checkType() {
return typeid(T) == value.type();
}
//Will cause an exception if the type doesn't match
//Caller should check type if unsure
template <typename T>
T get() {
if (checkType<T>()) {
return boost::any_cast<T>(value);
} else {
//throw some exception
throw bad_any_cast();
}
}
}
With this, I don't need to deal with destructors or copy/move functions, since the implicit ones will call the code already implemented by the boost library.
EDIT:
Thanks to milleniumbug for pointing out boost::any already stores the std::type_info
I have a class which has a template:
template<class T = int> class slider;
The class has a void Process(void) method, so, I think it should be callable regarless of the type, return value is void and there are no parameters to it.
As for now I have this code to call process each frame in my application:
//class menu:
typedef boost::variant<std::shared_ptr<slider<int>>,std::shared_ptr<slider<float>>,std::shared_ptr<slider<double>>,std::shared_ptr<slider<char>>> slider_type;
std::map<std::string,slider_type> Sliders;
//buttons ... etc ...
void Process()
{
if(!Sliders.empty())
{
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
case 0://slider<int>
{
boost::get<std::shared_ptr<slider<int>>>(i->second)->Process();
break;
}
case 1://slider<float>
{
boost::get<std::shared_ptr<slider<float>>>(i->second)->Process();
break;
}
//.....
}
}
}
}
Is it possible to execute the functions Process() like in the following example?
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
boost::get<???Any???>(i->second)->Process();
}
}
If yes, how?
What would such a function return? You can't change the type of a function at runtime. And the point of a variant is that it's contents are determined at runtime.
The only thing it could return is a boost::any. Which is really just exchanging one kind of unknown for another (an unknown that's a lot harder to deal with when you don't know what it contains, mind you). But if you want to see such a visitor:
struct convert_to_any : public boost::static_visitor<boost::any>
{
template<typename T> boost::any operator() (const T& t) {return t;}
};
Use apply_visitor on that, and you will get an any back. Though I fail to see how that's helpful.
In any case, if you're using get on a variant, you are almost certainly doing the wrong thing. The correct way to access the elements of a variant is with a visitor, not with get.
In your case, the visitor should be simple:
struct ProcessVisitor : public boost::static_visitor<>
{
template<typename T> void operator() (const T& t) const {t->Process();}
};
Just use apply_visitor on that. If the variant contains a type that can be used with operator-> and the return value of that function can have Process called on it, then it will.
(Untested code!)
struct CallProcess : static_visitor<>
{
template <class T>
void operator()(const T &t) const
{
t->Process();
}
};
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
boost::apply_visitor(CallProcess(), i->second);
}
No, not at all. You have to visit and deal with the case of every type. That is much better done with a visitor than your switch hack.
It's not possible because boost::variant has no way to know that all the types in the variant have anything in common. In fact, since the compiler generates a distinct class for each template specialization used, the address of the Process() function that would need to be used is different for each type in the boost::variant. To get around this you could abandon variant and use virtual functions and polymorphic classes sharing a common base class.