C++ compile time template polymorphism - c++

I would like to create an application in which I can define list of typed questions, then iterate over this list and ask user to enter answers, storing them in another list, then go over answers, validate them and provide a result.
My current naive approach is something like this:
class Question {
std::string message;
public:
Question(const std::string& msg) : message{msg} {}
std::string& getQuestion(void) const {
return this->message;
}
virtual ~Question(void) = 0;
};
Question::~Question(void) {}
template<class AnswerType>
class Prompt : public Question {
Prompt(std::string& msg) : Question{msg} {}
virtual ~Prompt(void) {}
};
class Answer<class T> {
T answ;
public:
Answer(const T& answer) : answ{answer} {}
T getAnswer(void) const {
return this->answ;
}
};
and I would like to do something like:
std::list< const Question* > whatToAsk{
new Prompt<int>{"Your age"},
new Prompt<std::string>{"Your name"},
new Prompt<float>{"Your weight"}
};
for(auto q in whatToAsk) {
Answer< "derived q template parameter" > a{};
std::cout << q->getQuestion() << ": ";
std::cin >> a;
// ... to be continued ...
}
storing questions (Prompt< T >) inside of std::list< const Question* >.
But problematic part for me is that I've to use downcasting (with runtime checking), virtual functions (runtime polymorphism) or double dispatch (again with runtime overhead).
My concern is that types are known during compilation, because list of questions will be hardcoded in the source code and I want to avoid runtime overhead and achieve compile-time static polymorphism.
How can I achieve this? Some kind of traits maybe?

If you don't want to use virtual functions/dynamic_casts you should
Make each question of a different type.
Store them in a tuple, not in a list.
Iterate using a special function (forEachArgument -- it's googleable).
It is much simpler to do it with virtual functions and unless you wil have thousands of questions the runtime overhead is negligible.

I'm not an expert but maybe you can use c++11 variadic templates that would allow you to have an array of different types, and thus would not cause any overhead due to downcast.
this link may interest you Create static array with variadic templates

A few sparse notes, more than a reply
Add the answer handling stuff section a member function of question. This way you can know the type of question. Something like
void ask()
{
Answer<T> answer ;
std::cint >> a ;
....
}
Use std::shared_ptr instead of plain pointers.
Probably an idiom known as virtual constructor as question factory can be helpful for question building
Something like
Question *make_question(int type)
{
switch (type)
{
case 0: return new Prompt<int>() ;
case 1: return new Prompt<std::string>() ;
...
}
}

If you are using c++14 you could do something like:
#include <iostream>
template <class FirstQuestion, class... OtherQuestions>
struct QuestionList {
template <class Functor>
void foreach(Functor &&functor) {
functor(FirstQuestion());
QuestionList<OtherQuestions...> oql;
oql.foreach(functor);
}
};
template <class FirstQuestion>
struct QuestionList<FirstQuestion> {
template <class Functor>
void foreach(Functor &&functor) {
functor(FirstQuestion());
}
};
template <class AnswerType, const char *QuestionString>
struct Question {
static AnswerType answer;
static void print_question() {
std::cout << QuestionString << std::endl;
}
static void get_answer() {
std::cin >> answer;
}
};
template <class AnswerType, const char *QuestionString>
AnswerType Question<AnswerType, QuestionString>::answer;
constexpr char questionstrings1[] = "lorem";
constexpr char questionstrings2[] = "ipsum";
int main() {
QuestionList<Question<int, questionstrings1>, Question<float, questionstrings2> > a;
a.foreach([](auto x){ x.print_question(); x.get_answer(); });
}
To access the answer in a loop you can simply:
a.foreach([](auto x){ /*doing something with x.answer*/ };
PS have in mind that prompting user for an answer kills potential efficiency of the non-runtime polymorhism...

Related

Can static polymorphism (templates) be used despite type erasure?

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.

Why would I want a .* operator in C++?

I recently found out that the .* operator (and the closely related ->* operator) exists in C++. (See this question.)
Seems neat at first, but why would I ever need such a thing? The two answers in the linked question provided contrived examples which would benefit from a direct function call.
Where a direct function call is inconvenient, a function object could be used instead, like the lambda functions that may be used in std::sort. This removes a level of indirection and hence would be more performant than using .*.
The linked question also mentioned a simplified version of this example:
struct A {
int a;
int b;
};
void set_member(A& obj, int A::* ptr, int val){
obj.*ptr = val;
}
int main()
{
A obj;
set_member(obj, &A::b, 5);
set_member(obj, &A::a, 7);
// Both members of obj are now assigned
}
But it's pretty trivial (perhaps even better practice because it's cleaner and isn't unnecessarily constrained to members of A) to do this instead:
struct A {
int a;
int b;
};
void set_me(int& out, int val){
out = val;
}
int main()
{
A obj;
set_me(obj.b, 5);
set_me(obj.a, 7);
// Both members of obj are now assigned
}
In conclusion, a pointer-to-member-function might be replaced by a function object, and a pointer-to-member-variable might be replaced by a direct reference of said variable or a function object. Doing so might also increase the efficiency of the code due to one less indirection.
This question only provides examples where my conclusion stands, so it does not answer my question.
Apart from interfacing legacy code which uses .* (in which there would be no choice at all), when, really, would I want to use .*?
Your example is too trivial to be illustrative. Consider a bit more complicated one
struct A {
int a;
int b;
};
void set_n_members(A objs[], unsigned n, int A::* ptr, int val)
{
for (unsigned i = 0; i < n; ++i)
objs[i].*ptr = val;
}
int main()
{
A objs[100];
set_n_members(objs, 100, &A::b, 5);
set_n_members(objs, 100, &A::a, 7);
}
How would you rewrite this without int A::* ptr and without inducing code bloat?
You could create collections of pointers to members and iterate over them. E.g.:
struct UserStrings
{
std::string first_name;
std::string surname;
std::string preferred_name;
std::string address;
};
...
std::array<std::string UserStrings::*, 4> str_cols = { &UserStrings::first_name, &UserStrings::surname, &UserStrings::preferred_name, &UserStrings::address };
std::vector<UserStrings> users = GetUserStrings();
for (auto& user : users)
{
for (auto& column : str_cols)
{
SanitizeForSQLQuery(user.*column);
}
}
It is used to implement std::mem_fn, which is used to implement std::function.
The following code shows how the ->* operator works in a naive Function class implemention.
Similarly, you can implement a member invoker class using the .* operator and a class reference.
#include <iostream>
class A
{
public:
void greet()
{
std::cout << "Hello world"<<std::endl;
}
};
template<typename R, typename ...TArgs>
class Invoker
{
public:
virtual R apply(TArgs&& ...args) = 0;
};
template<typename C, typename R, typename ...TArgs>
class MemberInvoker :public Invoker<R, TArgs...>
{
protected:
C* sender;
R(C::*function)(TArgs ...args);
public:
MemberInvoker(C* _sender, R(C::*_function)(TArgs ...args))
:sender(_sender)
, function(_function)
{
}
virtual R apply(TArgs&& ...args) override
{
return (sender->*function)(std::forward<TArgs>(args)...);
}
};
template<typename T>
class Func
{
};
template<typename R, typename ...TArgs>
class Func<R(TArgs...)>
{
public:
Invoker<R,TArgs...>* invoker=nullptr;
template<typename C>
Func(C* sender, R(C::*function)(TArgs...))
{
invoker =new MemberInvoker<C, R, TArgs...>(sender, function);
}
R operator()(TArgs&& ...args)
{
return invoker->apply(std::forward<TArgs>(args)...);
}
~Func()
{
if (invoker)
{
delete invoker;
invoker = nullptr;
}
}
};
int main()
{
A a;
Func<void()> greetFunc(&a, &A::greet);
greetFunc();
system("PAUSE");
}
Let's say you wanted to write a LINQ style library for C++ that could be used something like this:
struct Person
{
std::string first_name;
std::string last_name;
std::string occupation;
int age;
int children;
};
std::vector<Person> people = loadPeople();
std::vector<std::string> result = from(people)
.where(&Person::last_name == "Smith")
.where(&Person::age > 30)
.select("%s %s",&Person::first_name,&Person::last_name);
for(std::string person : result) { ... };
Under the covers, the where function accepts an expression tree containing a pointer to member (among other stuff) and is applied to each vector item looking for one that matches. The select statement accepts a format string and some pointer to members and does an sprintf style formatting of whichever vector items get through the where statements.
I have written something like this, and there are several others out there that do it slightly differently (Is there a LINQ library for C++?). Pointer-to-member allows the library user to specify whichever members of their struct that they want and the library doesn't need to know anything about what they might do.

Call function on boost::variant regardless of type?

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.

Making a tuple style class that optimizes out unused sections

This is more of a question of how the C++ compiler handles const typeid calls.
Hello! I am trying to make a tuple-style class, configured in such a way that I don't have to rewrite a bunch of the code with specializations.
So this is the general idea:
struct null_type{};
template <typename T1,typename T2=null_type,typename T3=null_type>
class ptestclass
{
private:
template<typename K1,typename K2,typename K3>
class barclass
{
public:
static inline void bar(std::tuple<K1,K2,K3>& vals,K1* otherval1,K2* otherval2,K3* otherval3)
{
Foo(tr1::get<0>(vals),*otherval1);
Foo(tr1::get<1>(vals),*otherval2);
Foo(tr1::get<2>(vals),*otherval3);
}
};
template<typename K1,typename K2>
class barclass<K1,K2,null_type>
{
public:
static inline void bar(std::tuple<K1,K2,null_type>& vals,K1* otherval1,K2* otherval2,null_type* otherval3)
{
Foo(tr1::get<0>(vals),*otherval1);
Foo(tr1::get<1>(vals),*otherval2);
}
};
template<typename K1>
class barclass<K1,null_type,null_type>
{
public:
static inline void bar(std::tuple<K1,null_type,null_type>& vals,K1* otherval1,null_type* otherval2,null_type* otherval3)
{
Foo(tr1::get<0>(vals),*otherval1);
}
};
/*
*Old Bar function...much more readable than bar class, but you cannot partially specialize
*member functions of a class
*
void inline bar(std::tuple<T1,T2,T3> otherval)
{
if (typeid(T1) != typeid(null_type))//constant check hopfully optomized out
{
Foo(vals.get(1),otherval.get(1));
}
if (typeid(T2) != typeid(null_type))//constant check hopfully optomized out
{
Foo(vals.get(2),otherval.get(2));
}
if(typeid(T3) != typeid(null_type))//constant check hopfully optomized out
{
Foo(vals.get(3),otherval.get(3));
}
}
*/
std::tuple<T1,T2,T3> vals;
template<typename K>
void static inline Foo(K& val,K& otherval)
{
//inlineable, short function that is called many (millions) of times per iteration
val += otherval;
}
template<>
void inline Foo<null_type>(null_type& val,null_type& otherval)
{
//inlineable, short function that is called many (millions) of times per iteration
throw "Foo called on null type";
}
public:
ptestclass()
{
printf("made object");
}
void one_iteration(T1* otherval1,T2* otherval2,T3* otherval3,size_t count)
{
for (int i = 0; i < count; ++i)
{
barclass<T1,T2,T3>::bar(vals,otherval1+i,otherval2+i,otherval3+i);
}
}
};
//exposed public class with specialized one_iteration interfaces
template <typename T1,typename T2=null_type,typename T3=null_type>
class testclass : public ptestclass<T1,T2,T3>
{
public:
void one_iteration(T1* otherval1,T1* otherval2,T1* otherval3,size_t count)
{
ptestclass::one_iteration(otherval1,otherval2,otherval3,count);
}
};
template <typename T1>
class testclass<T1,null_type,null_type> : public ptestclass<T1,null_type,null_type>
{
public:
void one_iteration(T1* otherval1,size_t count)
{
ptestclass::one_iteration(otherval1,NULL,NULL,count);
}
};
So my question is is this optimization even possible within C++? If not, it will probably make more sense for me to use an inheritance model on the child nodes rather then a template at this level. However, I am trying to avoid the continual check of the number of types specified and the cost of indirection.
I'm going to start diving into the assembly to see if that is what the compiler does...Just in case this is not standardized behavior, I'm using the Microsoft Visual C++ Compiler 10.0.
I think I misunderstood your question when I put my earlier comment.
Assuming you can use c++11, or you can use boost, you could use something like !std::is_same< T1, null_type >::value /*or boost::is_same...*/ instead of typeid(T1) != typeid(null_type). This uses TMP to resolve to a compile-time constant, which most compilers would have no trouble optimizing away.
This is more of a question of how the C++ compiler handles const typeid calls.
I didn't answer this specific question, but if I understand what you were actually looking for, the above should suffice.

Runtime value to type mapping

I've got a list of types which can be send over the network, take this example:
enum types {
E_T1,
E_T2,
E_T3,
E_T4
};
Now I have a list of classes which correspond to each of the types, let's say each is declared as class E_T1 {...}, class E_T2 {...}, etc.
They are not derived from a common base class and it's not possible to do so. Each of the classes has a verification method I need to invoke with the data send over the network. The client sends the data D and a id correspointing to the message type. I need to get hold of the object corresponding to the type. I can use C++0x features if needed.
What I've tried so far is using specialized templates for the types, holding a typedef for the object related to it. This was obviously a stupid idea as templates parameters need to be compile time constant so doing something along getType<data.id()>::type is not possible.
Then I tried using Boost.Variant to get a common returnable type like this (used mpl vector to iterate over the registered types at runntime for debbuging):
template <typename C>
struct getType() {
typedef C type;
}
typedef boost::mpl::vector<
getType<E_T1>,
getType<E_T2>,
getType<E_TX>...
> _types;
typedef boost::make_variant_over<_types>::type _type;
//use a map to store each type <-> id
boost::unorderd_map<types, _type> m;
m[E_T1] = getType<E_T1>();
m[data.id()]::type x; //<- access type, can now call x.validate(data)
The problem with this is that it's limited to 20 entries per variant per default. This can be overwritten but from what I understood the overhead per type should be considered and we are talking about a few thousand types here.
Also tried boost.any but it doesn't hold any type information so that's out of the question again. Has anyone any good ideas how this can be solved elegantly?
Looking for something where I don't have to write a 1k switch statement anytime I handle a type.
All types are nown at compile type, same goes for their corresponding IDs.
Id -> Type resolving needs to happen at runtime though.
Thanks in advance,
Robin.
External Polymorphism (*)
It's a widely known idiom, however it's widely used: I first encountered it in the shared_ptr implementation and it's been quite useful in my toolbox.
The idea is to actually create a base class for all those types. But not having them derive from it directly.
class Holder {
public:
virtual ~Holder() {}
virtual void verify(unsigned char const* bytes, size_t size) const = 0;
}; // class Holder
template <typename T>
class HolderT: public Holder {
public:
HolderT(): _t() {}
virtual void verify(unsigned char const* bytes, size_t size) const {
_t.verify();
}
private:
T _t;
}; // class HolderT
template <typename T>
std::unique_ptr<Holder> make_holder() {
return std::unique_ptr<Holder>(new HolderT<T>());
}
So, it's the classic strategy of adding a new level of indirection.
Now, you obviously do need a switch to move from value to class. Or perhaps... a map ?
using maker = std::unique_ptr<Holder> (&)();
using maker_map = std::unordered_map<types, maker>;
std::unique_ptr<Holder> select(types const E) {
static maker_map mm;
if (mm.empty()) {
mm.insert(std::make_pair(E_T1, make_holder<EC_T1>));
// ...
}
maker_map::const_iterator it = mm.find(E);
if (it == mm.end()) { return std::unique_ptr<Holder>(); }
return (*it->second)();
}
And now you can handle them polymorphically:
void verify(types const E, unsigned char const* bytes, size_t size) {
std::unique_ptr<Holder> holder = select(E);
if (not holder) { std::cerr << "Unknown type " << (int)E << "\n"; return; }
holder->verify(bytes, size);
}
Of course, you're welcome to make the strategy vary according to your needs. For example moving the map out of select so that you can register your types dynamically (like for plugins).
(*) At least that's the name I have for it, I would quite happy to find out it's already been named.
I'll assume you have a generic way of handling a message, such as for example an overloaded function:
void handle_message(const E_T1& msg);
void handle_message(const E_T2& msg);
//...
Now, you do not really need to get the object's type. All you need is a way to handle a message of that type, given the undecoded message.
So, I recommend you populate a map of factory functions:
std::unordered_map<types, std::function<void (unsigned char const* bytes, size_t size)> handlers;
handlers[E_E1] = [](unsigned char const* bytes, size_t size) { handle_message(E_T1(bytes, size)); };
// ...
Then, once you've decoded the type, you can use handlers[type](bytes, size) to decode and handle a message.
Try variadic templates and your already defined getType class:
enum types { T1_ID, T2_ID, .... };
class T1; class T2; class T3; ....
template <types t> struct getType;
template <> struct getType<T1_ID> { typedef T1 type; };
template <> struct getType<T2_ID> { typedef T2 type; };
...
And the operation verify:
template <types...>
struct type_operation;
template <types t1, types... rest>
struct type_operation<t1, rest...>
{
void verify(types t)
{
if (t == t1)
{
typename getType<t1>::type a;
a.verify(); // read from network and verify the rest of data....
}
else type_operation<rest...>::verify(t, data);
}
};
template <>
struct type_operation<>
{
void verify(types t)
{
ostringstream log; log << "not suppoted: " << t;
throw std::runtime_error(log.str()); //
}
};
Usage:
typedef type_operation<T1_ID, T2_ID, T3_ID, ,,.., TN_ID> type_mapping;
types id;
readFromNetwork(id);
type_mapping::verify(id);