I have a class that contains several vectors of unrelated classes.
class Class0 {};
class Class1 {};
class Class2 {};
enum class RemoveFromVector : uint8_t { CLASS0 = 0, CLASS1 = 1, CLASS2 = 2 };
class C
{
public:
std::vector<Class0> c0s;
std::vector<Class1> c1s;
std::vector<Class2> c2s;
void RemoveFromVector(const RemoveFromVector RFV, const int Index)
{
// I'd like to replace this switch with something that's compile-time
switch ((uint8_t)RFV)
{
case 0: c0s.erase(c0s.begin() + Index); break;
case 1: c1s.erase(c1s.begin() + Index); break;
case 2: c2s.erase(c2s.begin() + Index); break;
default: break;
}
}
};
int main()
{
C c;
c.c0s.push_back(Class0());
c.c1s.push_back(Class1());
c.c1s.push_back(Class1());
c.c1s.push_back(Class1());
c.c2s.push_back(Class2());
// this should remove the last element from C.c1s
c.RemoveFromVector(RemoveFromVector::CLASS1, 2);
}
How would I write a function that removes an element from one of the vectors based on an enum (or int) at runtime without having to write a switch that's got a case for every single vector?
In other words, I'm looking for a way to deduce which vector to call erase() on statically at compile-time, which I then call at runtime. The correct term might be "static dispatch" though I'm not entirely certain.
You should keep the code as much as simple as possible. As per the currently shown code, it is simple and readable for every developer who works later on the codebase.
Secondly, the internal storage via std::vectors will make this task anyways at run-time. Because, most of the operations happen with the std::vector is run time overhead as they allocate the memory and manage it at run time. Therefore you can not do any compile-time work for std::vector::erase and whatsoever.
That being said, if you insist to avoid the switch statement, and bring the template complication, one is below, which still would have kind of template type mapping using if constexpr, and the vector erase happens at run-time.
#include <type_traits> // std::is_same_v
class C
{
template<typename ClassType>
auto& getVectorOf() /* noexcept */
{
if constexpr (std::is_same_v<ClassType, Class0>) return c0s;
else if constexpr (std::is_same_v<ClassType, Class1>) return c1s;
else if constexpr (std::is_same_v<ClassType, Class2>) return c2s;
}
public:
std::vector<Class0> c0s; // recommended to be private!
std::vector<Class1> c1s;
std::vector<Class2> c2s;
template<typename ClassType>
void RemoveFromVector(const std::size_t index) /* noexcept */
{
// some index check!
auto& vec = getVectorOf<ClassType>();
vec.erase(vec.begin() + index);
}
};
Call the function like
C c;
// fill the vectors
c.RemoveFromVector<Class0>(0);
c.RemoveFromVector<Class1>(2);
c.RemoveFromVector<Class2>(0);
(See a Demo Online)
Here's a slightly more generic solution:
template <typename... Types>
class MultiStack
{
public:
template <typename T>
/*const*/ T& Get() /*const*/
{
return GetStack<T>().back();
}
template <typename T>
void Push( const T& t )
{
GetStack<T>().push_back( t );
}
template <typename T>
void Pop()
{
GetStack<T>().pop_back();
}
template <size_t... Sizes>
void Reserve()
{
auto reserve = [&]( auto&... stacks ) { ( stacks.reserve( Sizes ), ... ); };
std::apply( reserve, Stacks );
}
private:
template <typename T>
std::vector<T>& GetStack()
{
return std::get<std::vector<T>>( Stacks );
}
std::tuple<std::vector<Types>...> Stacks;
};
Usage looks nice and simple:
using MyStack = MultiStack<Class0,Class1,Class2>;
MyStack stack;
stack.Push( ClassX() ); // automatically pushes any compatible object on the appropriate stack
stack.Pop<Class1>(); // pops/"erases" last object of the Class1 stack (vector)
You could extend this if you need to Pop more than one object at a time, or 'call other functions' on the vector. You could/should also make Get return const T& depending on your needs. (Get() const will need GetStack() const)
I have left in the fancy Reserve() just to show off. ;-) You will probably want to set certain but different initial sizes for your stacks.
I think std::variant and std::visit are the way you can go. You also can use a new helper function make_variant_array to make the code even shorter:
#include <cstdint>
#include <vector>
#include <variant>
#include <array>
#include <functional>
class Class0 {};
class Class1 {};
class Class2 {};
enum class RemoveFromVector
: uint8_t
{
Class0 = 0, Class1 = 1, Class2 = 2
};
template <class... Args>
auto make_variant_array(Args&... args)
{
using var_t = std::variant<std::reference_wrapper<Args>...>;
return std::array<var_t, sizeof...(Args)>{std::ref(args)...};
}
class C
{
public:
std::vector<Class0> c0s;
std::vector<Class1> c1s;
std::vector<Class2> c2s;
void RemoveFromVector(const RemoveFromVector RFV, const int Index)
{
static auto lookup = make_variant_array(c0s, c1s, c2s);
std::visit([Index](auto& vec) { vec.get().erase(vec.get().begin() + Index); }, lookup[(uint8_t)RFV]);
}
};
int main()
{
C c;
c.c0s.push_back(Class0());
c.c1s.push_back(Class1());
c.c1s.push_back(Class1());
c.c1s.push_back(Class1());
c.c2s.push_back(Class2());
// this should remove the last element from C.c1s
c.RemoveFromVector(RemoveFromVector::Class1, 2);
}
Related
I want to use a map to refer to a type specifier mainly to shorten my code from multiple uses of
std::unique_ptr< Class >(new class1);
to
std::unique_ptr< Class >(new sampleMap[enum1]);
and then define my map so that it refers each enum value (enum1, enum2, ...) to my classes (class1, class2, ...).
But I cannot define my map with the values being a type name like this
std::map < int, Class > mapName {
{0, class1},
{0, class1},
...
};
since type name is not allowed in maps.
The main reason I'm looking for an answer for this is to make my code more succinct by replacing a series of "if/else if" statements or "switch-case" statements into only one line of code where the output std::unique_ptr<Class>(new class1); is dynamically figured out through the map that I define. So, I just input the enum number and get the corresponding class instantiated for me. Otherwise, I would have to do this:
if (enum1 = 0)
{
std::unique_ptr< Class >(new class1);
}
else if (enum2 = 0)
{
std::unique_ptr< Class >(new class2);
}
(or a switch-case)
But I want to do all above in one line like this:
std::unique_ptr<Class>(new sampleMap[enum1]);
plus the map declaration.
Any clue how this could be done?
You cannot easily implement an std::map that will return types as values the way you are trying to do it. You would need to implement your own class that would represent types as values. However, since your goal seems to be to create instances of objects where the concrete type depends on a value, an easily solution is to make a map of functions instead. This assumes that all the types you want to support derive from a common type. Each value can hold a function which constructs the correct object. If your types do not derive from a common type, then you will need to preform further type erasure (perhaps with std::any).
#include <functional>
#include <iostream>
#include <map>
#include <memory>
// Simple set of classes
// Class is the base type
// Class1 and Class2 derive from Class
struct Class { virtual void func() = 0; };
struct Class1 : Class {
void func() override { std::cout << "Class1\n"; }
};
struct Class2 : Class {
void func() override { std::cout << "Class2\n"; }
};
// A map of factory functions
const std::map<int, std::function<std::unique_ptr<Class>()>> mapName = {
{ 1, []() {return std::make_unique<Class1>(); } },
{ 2, []() {return std::make_unique<Class2>(); } }
};
int main()
{
auto foo = mapName.at(2)(); // Make an object of type associated with the value 2
foo->func(); // Prints "Class2\n"
return 0;
}
Depending on where you want to use this code, you might want to do this with an if-else chain. std::functions are usually very difficult for the compiler to optimize, so if you expect this code to be called frequently enough, it's probably more efficient to just code it out:
(using #FrançoisAndrieux's example)
#include <iostream>
#include <memory>
#include <stdexcept>
// Simple set of classes
// Class is the base type
// Class1 and Class2 derive from Class
struct Class {
virtual void func() = 0;
};
struct Class1 : Class {
void func() override { std::cout << "Class1\n"; }
};
struct Class2 : Class {
void func() override { std::cout << "Class2\n"; }
};
std::unique_ptr<Class> make_class(int i)
{
if (i == 0) return std::make_unique<Class1>();
else if (i == 1) return std::make_unique<Class2>();
throw std::out_of_range{ "Asked to construct an unknown type" };
}
int main()
{
auto foo = make_class(1); // Make an object of type associated with the value 1
foo->func(); // Prints "Class2\n"
return 0;
}
If the number of values is large, you might gain by doing a binary search (or just a switch):
// If there are 128 elements, for example
if (!(0 <= i && i < 128)) throw std::out_of_range{ "..." };
if (i < 64) {
if (i < 32) {
...
} else {
...
}
} else {
...
}
It's messy but it's only in one place.
To make a more optimizable version, you can do some minimal metaprogramming / expression templates:
#include <iostream>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
// Simple set of classes
// Class is the base type
// Class1 and Class2 derive from Class
struct Class {
virtual void func() = 0;
};
struct Class1 : Class {
void func() override { std::cout << "Class1\n"; }
};
struct Class2 : Class {
void func() override { std::cout << "Class2\n"; }
};
template<typename R, typename SwBase, typename T, typename F>
struct Switch
{
SwBase base;
T value;
F fn;
constexpr Switch(SwBase base, T value, F fn)
: base{ std::move(base) }
, value{ std::move(value) }
, fn{ std::move(fn) }
{}
constexpr R operator()(T val) const {
if (value == val) return fn();
return base(val);
}
};
template<typename R, typename SwBase, typename T, typename F>
constexpr auto make_switch_impl(SwBase&& swb, T&& t, F&& f)
{
return Switch<R, std::decay_t<SwBase>, std::decay_t<T>, std::decay_t<F>> {
std::forward<SwBase>(swb),
std::forward<T>(t),
std::forward<F>(f),
};
}
template<typename R>
constexpr auto make_switch(char const* failMsg)
{
return [=](auto&&) -> R { throw std::out_of_range{ failMsg }; };
}
template<typename R, typename T, typename F, typename... Args>
constexpr auto make_switch(char const* failMsg, T&& val, F&& fn, Args&&... args)
{
return make_switch_impl<R>(
make_switch<R>(failMsg, std::forward<Args>(args)...),
std::forward<T>(val),
std::forward<F>(fn)
);
}
auto make_class(int i)
{
return make_switch<std::unique_ptr<Class>>(
"Asked to construct an unknown type",
0, [] { return std::make_unique<Class1>(); },
1, [] { return std::make_unique<Class2>(); }
)(i);
}
int main()
{
auto foo = make_class(1); // Make an object of type associated with the value 1
foo->func(); // Prints "Class2\n"
return 0;
}
The switch statement would turn into this:
auto make_class(int i)
{
return make_switch<std::unique_ptr<Class>>(
"Asked to construct an unknown type",
0, [] { return std::make_unique<Class1>(); },
1, [] { return std::make_unique<Class2>(); }
)(i);
}
You could also store the "switch" separately, although this makes it less optimizable (down to roughly the same level as François Andrieux's solution):
const auto mapName = make_switch<std::unique_ptr<Class>>(
"Asked to construct an unknown type",
0, [] { return std::make_unique<Class1>(); },
1, [] { return std::make_unique<Class2>(); }
);
auto make_class(int i)
{
return mapName(i);
}
This version, and also raw if-else chains, let the compiler optimize the make_class function to the equivalent of a switch statement. Also, the main function:
int main()
{
auto foo = make_class(1); // Make an object of type associated with the value 1
foo->func(); // Prints "Class2\n"
return 0;
}
Can be optimized to the equivalent of:
int main()
{
std::cout << "Class2\n";
return 0;
}
Whereas storing the std::function or the other less efficient tricks I've mentioned makes it much more difficult for the compiler to optimize it fully (I haven't found one that does).
Note that out of GCC, Clang, Visual C++, and the Intel compiler, only Clang was able to completely optimize the main function using this Switch struct. GCC and Visual C++ were able to optimize it to a call to Class2's func(). The Intel compiler doesn't seem to have optimized it at all (but maybe I don't know the right flags for it)
Suppose I have this class :
class Component1;
class Component2;
// many different Components
class Component42;
class MyClass
{
public:
MyClass(void) {};
std::list<Component1> component1List;
std::list<Component2> component2List;
// one list by component
std::list<Component42> component42List;
};
I would like to create a function with the following signature:
template<class T> void addElement(T component);
It should do the following:
if component is of type Component1, add it to Component1List
if component is of type Component2, add it to Component2List, etc.
Is it possible? What's a good way to do this?
I can obtain the same behaviour with a function like :
template<class T> void addElement(int componentType, T component);
but I'd rather not have to specify the componentType like this : it's useless information and it open the door to possible errors (if componentType doesn't represent the type of component).
std::tuple to the rescue.
changelog:
use std::decay_t
added the variadic argument form
add_component() now returns a reference to this to allow call-chaining.
#include <iostream>
#include <list>
#include <utility>
#include <type_traits>
#include <tuple>
class Component1 {};
class Component2 {};
struct Component3 {
Component3() {}
};
// many different Components
template<class...ComponentTypes>
class MyClassImpl
{
template<class Component> using list_of = std::list<Component>;
public:
using all_lists_type =
std::tuple<
list_of<ComponentTypes> ...
>;
// add a single component
template<class Component>
MyClassImpl& add_component(Component&& c)
{
list_for<Component>().push_back(std::forward<Component>(c));
return *this;
}
// add any number of components
template<class...Components>
MyClassImpl& add_components(Components&&... c)
{
using expand = int[];
void(expand { 0, (void(add_component(std::forward<Components>(c))), 0)... });
return *this;
}
template<class Component>
auto& list_for()
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
template<class Component>
const auto& list_for() const
{
using component_type = std::decay_t<Component>;
return std::get<list_of<component_type>>(_lists);
}
private:
all_lists_type _lists;
};
using MyClass = MyClassImpl<Component1, Component2, Component3>;
int main()
{
MyClass c;
c.add_component(Component1());
c.add_component(Component2());
const Component3 c3;
c.add_component(c3);
c.add_components(Component1(),
Component2(),
Component3()).add_components(Component3()).add_components(Component1(),
Component2());
std::cout << c.list_for<Component1>().size() << std::endl;
return 0;
}
The most straightforward variant is to simply not use templates but to overload the addElement() function:
void addElement(Component1 element)
{
this->element1List.push_back(element);
}
void addElement(Component2 element)
{
this->element2List.push_back(element);
}
// ... etc
However, this might get tedious if you have many of these (and you don't just have addElement(), I guess). Using a macro to generate the code for each type could still do the job with reasonable effort.
If you really want to use templates, you could use a template function and specialize the template function for each type. Still, this doesn't reduce the amount of code repetition when compared with the above approach. Also, you could still reduce it using macros to generate the code.
However, there's hope for doing this in a generic way. Firstly, let's create a type that holds the list:
template<typename T>
struct ComponentContainer
{
list<T> componentList;
};
Now, the derived class just inherits from this class and uses C++ type system to locate the correct container baseclass:
class MyClass:
ComponentContainer<Component1>,
ComponentContainer<Component2>,
ComponentContainer<Component3>
{
public:
template<typename T>
void addElement(T value)
{
ComponentContainer<T>& container = *this;
container.componentList.push_back(value);
}
}
Notes here:
This uses private inheritance, which is very similar to the containment you originally used.
Even though ComponentContainer is a baseclass, it doesn't have any virtual functions and not even a virtual destructor. Yes, this is dangerous and should be documented clearly. I wouldn't add a virtual destructor though, because of the overhead it has and because it shouldn't be needed.
You could drop the intermediate container altogether and derive from list<T>, too. I didn't because it will make all of list's memberfunctions available in class MyClass (even if not publicly), which might be confusing.
You can't put the addElement() function into the base class template to avoid the template in the derived class. The simple reason is that the different baseclasses are scanned in order for a addElement() function and only then overload resolution is performed. The compiler will only find the addElement() in the first baseclass therefore.
This is a plain C++98 solution, for C++11 I'd look at the type-based tuple lookup solutions suggested by Jens and Richard.
If there are not too many classes you could go with overloading. A template-based solution could be done with type-based lookup for tuples:
class MyClass {
public:
template<typename T> void addElement(T&& x) {
auto& l = std::get<std::list<T>>(lists);
l.insert( std::forward<T>(x) );
}
private:
std::tuple< std::list<Component1>, std::list<Component2> > lists;
};
If you don't know in advance the types you will need storing when instantiating the multi-container an option is to hide the types and using type_index to keep a map of lists:
struct Container {
struct Entry {
void *list;
std::function<void *(void*)> copier;
std::function<void(void *)> deleter;
};
std::map<std::type_index, Entry> entries;
template<typename T>
std::list<T>& list() {
Entry& e = entries[std::type_index(typeid(T))];
if (!e.list) {
e.list = new std::list<T>;
e.deleter = [](void *list){ delete ((std::list<T> *)list); };
e.copier = [](void *list){ return new std::list<T>(*((std::list<T> *)list)); };
}
return *((std::list<T> *)e.list);
}
~Container() {
for (auto& i : entries) i.second.deleter(i.second.list);
}
Container(const Container& other) {
// Not exception safe... se note
for (auto& i : other.entries) {
entries[i.first] = { i.second.copier(i.second.list),
i.second.copier,
i.second.deleter };
}
};
void swap(Container& other) { std::swap(entries, other.entries); }
Container& operator=(const Container& other) {
Container(other).swap(*this);
return *this;
};
Container() { }
};
that can be used as:
Container c;
c.list<int>().push_back(10);
c.list<int>().push_back(20);
c.list<double>().push_back(3.14);
NOTE: the copy constructor as written now is not exception safe because in case a copier throws (because of an out of memory or because a copy constructor of an element inside a list throws) the already allocated lists will not be deallocated.
void addElement(Component1 component) {
componentList1.insert(component);
}
void addElement(Component2 component) {
componentList2.insert(component);
}
I'd like to fill in the store() and launch() methods in the below code. The important detail which captures the spirit of the problem is that the object foo declared in main() no longer exists at the time we call launch(). How can I do this?
#include <cstdio>
#include <cstring>
#include <type_traits>
template<typename T, typename U=
typename std::enable_if<std::is_trivially_copyable<T>::value,T>::type>
struct Launchable {
void launch() { /* some code here */ }
T t;
// other members as needed to support DelayedLauncher
};
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
// copy-construct/memcpy t into some storage
}
void launch() const {
// call t.launch(), where t is (a copy of) the last value passed into store()
}
// other members as needed
};
int main() {
DelayedLauncher launcher;
{
Launchable<int> foo;
launcher.store(foo);
}
launcher.launch(); // calls foo.launch()
return 0;
}
Note that if we only had a fixed set of N types to pass into store(), we could achieve the desired functionality by declaring N Launchable<T> fields and N non-template store() methods, one for each type, along with an enum field whose value is use in a switch statement in the launch() method. But I'm looking for an implementation of DelayedLauncher that will not need modification as more Launchable types are added.
using std::function:
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
f = [t]() {t.launch();};
}
void launch() const { f(); }
private:
std::function<void()> f;
};
You could give Launchable a base class with a virtual launch() and no template, and store pointers to that base class in Launcher::store.
EDIT: Adapted from #dshin's solution:
struct LaunchableBase {
virtual void launch() = 0;
};
template<typename T, typename U=
typename std::enable_if<std::is_trivially_copyable<T>::value,T>::type>
struct Launchable : public LaunchableBase {
virtual void launch() override { /* some code here */ }
T t;
// other members as needed to support DelayedLauncher
};
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
static_assert(sizeof(t) <= sizeof(obj_buffer),
"insufficient obj_buffer size");
static_assert(std::is_trivially_destructible<T>::value,
"leak would occur with current impl");
p = new (obj_buffer) Launchable<T>(t);
}
void launch() const {
p->launch();
}
private:
char obj_buffer[1024]; // static_assert inside store() protects us from overflow
LaunchableBase *p;
};
I believe this variant of Jarod42's solution will avoid dynamic allocation, although I would appreciate if someone could confirm that this will work the way I think it will:
class DelayedLauncher {
public:
template<typename T>
void store(const Launchable<T>& t) {
static_assert(sizeof(t) <= sizeof(obj_buffer),
"insufficient obj_buffer size");
static_assert(std::is_trivially_destructible<T>::value,
"leak would occur with current impl");
auto p = new (obj_buffer) Launchable<T>(t);
auto ref = std::ref(*p);
f = [=]() {ref.get().launch();};
}
void launch() const {
f();
}
private:
char obj_buffer[1024]; // static_assert inside store() protects us from overflow
std::function<void()> f;
};
I believe it should work because the resources I've looked at indicate that std::function implementations typically have a "small capture" optimization, only triggering a dynamic allocation if the total size of the captured data exceeds some threshold.
EDIT: I replaced my code with a version provided by Jarod42 in the comments. The standard guarantees the above implementation will not trigger dynamic allocation.
I'm using something like:
struct VectorCache
{
template<typename T>
std::vector<T>& GetTs()
{
static std::vector<T> ts;
return ts;
}
};
to create/access some vectors based on the contained type. This works fine as long as I have only one object of type VectorCache, but when I use multiple objects I will get same vectors from all instances of VectorCache as the vectors are static variables.
I tried to move the vectors as member variables using something similar to boost::any and access them using std::type_index of T, but this is somehow slower than the direct access I used before.
Another options is to transform struct VectorCache to something like template<int index> struct VectorCache, but the problem is still there - I will have to be careful to have only one instance/index to have correct behaviour.
Is it possible to access the vectors directly based on T and also have the caching instance based instead of class based?
You could try an unchecked analogue of Boost.Any. See if that's fast enough for you (though I don't believe it would make a big difference):
#include <memory>
#include <type_traits>
#include <typeindex>
#include <unordered_map>
#include <vector>
class AnyCache
{
struct TEBase
{
virtual ~TEBase() {}
virtual void * get() = 0;
};
template <typename T> struct TEObject : TEBase
{
T obj;
virtual void * get() override { return static_cast<void *>(&obj); }
};
std::unordered_map<std::type_index, std::unique_ptr<TEBase>> cache;
public:
AnyCache(AnyCache const &) = delete;
AnyCache & Operator=(AnyCache const &) = delete;
template <typename T> decltype(auto) get()
{
using U = std::decay_t<T>;
using C = std::vector<U>;
std::unique_ptr<TEBase> & p = cache[typeid(U)];
if (!p) { p = std::make_unique<TEObject<C>>(); }
return *static_cast<C *>(p->get());
}
};
Usage:
AnyCache ac;
ac.get<int>().push_back(20);
ac.get<std::string>().push_back("Hello");
for (auto const & x : ac.get<Foo>()) { std::cout << x << '\n'; }
If - and it's a big if - your VectorCache-using code isn't threaded, you can do this:
struct VectorCache
{
VectorCache() : instance_counter_(++s_instance_counter_) { }
template<typename T>
std::vector<T>& GetTs()
{
static std::vector<std::vector<T>> tss;
if (tss.size() <= instance_counter_)
tss.resize(instance_counter_);
return tss[instance_counter_];
}
size_t instance_counter_;
static size_t s_instance_counter_;
};
// and define size_t VectorCache::s_instance_counter_;
implementation on ideone.com
With a little synchronisation you can make it thread safe, or even thread specific if that suits. Add deletion of copy construction / assignment etc. if that makes sense in your intended usage.
suppose you have some code like this:
struct Manager
{
template <class T>
void doSomething(T const& t)
{
Worker<T> worker;
worker.work(t);
}
};
A "Manager" object is created once and called with a few diffent types "T", but each type T is called many times. This might be, in a simplified form, like
Manager manager;
const int N = 1000;
for (int i=0;i<N;i++)
{
manager.doSomething<int>(3);
manager.doSomething<char>('x');
manager.doSomething<float>(3.14);
}
Now profiling revealed that constructing a Worker<T> is a time-costly operation and it should be avoided to construct it N times (within doSomething<T>). For thread-safety reasons it is ok to have one Worker<int>, one Worker<char> and Worker<float> per "Manager", but not one Worker<int> for all Managers. So usually I would make "worker" a member variable. But how could I do this in the code above? (I do not know in advance which "T"s will be used).
I have found a solution using a std::map, but it is not fully typesafe and certainly not very elegant. Can you suggest a typesafe way without constructing Worker<T> more often than once per "T" without virtual methods?
(please note that Worker is not derived from any template-argument free base class).
Thanks for any solution!
You can use something like a std::map<std::type_info,shared_ptr<void> > like this:
#include <map>
#include <typeinfo>
#include <utility>
#include <functional>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;
// exposition only:
template <typename T>
struct Worker {
void work( const T & ) {}
};
// wrapper around type_info (could use reference_wrapper,
// but the code would be similar) to make it usable as a map<> key:
struct TypeInfo {
const type_info & ti;
/*implicit*/ TypeInfo( const type_info & ti ) : ti( ti ) {}
};
// make it LessComparable (could spcialise std::less, too):
bool operator<( const TypeInfo & lhs, const TypeInfo & rhs ) {
return lhs.ti.before( rhs.ti );
}
struct Manager
{
map<TypeInfo,shared_ptr<void> > m_workers;
template <class T>
Worker<T> * findWorker()
{
const map<TypeInfo,shared_ptr<void> >::const_iterator
it = m_workers.find( typeid(T) );
if ( it == m_workers.end() ) {
const shared_ptr< Worker<T> > nworker( new Worker<T> );
m_workers[typeid(T)] = nworker;
return nworker.get();
} else {
return static_cast<Worker<T>*>( it->second.get() );
}
}
template <typename T>
void doSomething( const T & t ) {
findWorker<T>()->work( t );
}
};
int main() {
Manager m;
m.doSomething( 1 );
m.doSomething( 1. );
return 0;
}
This is typesafe because we use type_info as an index into the map. Also, the workers are properly deleted even though they're in shared_ptr<void>s because the deleter is copied from the original shared_ptr<Worker<T> >s, and that one calls the proper constructor. It also doesn't use virtual functions, although all type erasure (and this is one) uses something like virtual functions somewhere. Here, it's in shared_ptr.
Factoring the template-independent code from findWorker into a non-template function to reduce code bloat is left as an exercise for the reader :)
Thanks to all commenters who pointed out the mistake of using type_info as the key directly.
You can add std::vector of boost::variants or boost::anys as member of your class. And append to it any worker you want.
EDIT: The code bellow will explain how
struct Manager
{
std::vector<std::pair<std::type_info, boost::any> > workers;
template <class T>
void doSomething(T const& t)
{
int i = 0;
for(; i < workers.size(); ++i)
if(workers[i].first == typeid(T))
break;
if(i == workers.size())
workers.push_back(std::pair<std::type_info, boost::any>(typeid(T).name(), Worker<T>());
any_cast<T>(workers[i]).work(t);
}
};
I was already working on an answer similar to mmutz's by time he posted his. Here's a complete solution that compiles and runs under GCC 4.4.3. It uses RTTI and polymorphism to lazily construct Worker<T>s and store them in a map.
#include <iostream>
#include <typeinfo>
#include <map>
struct BaseWorker
{
virtual ~BaseWorker() {}
virtual void work(const void* x) = 0;
};
template <class T>
struct Worker : public BaseWorker
{
Worker()
{
/* Heavyweight constructor*/
std::cout << typeid(T).name() << " constructor\n";
}
void work(const void* x) {doWork(*static_cast<const T*>(x));}
void doWork(const T& x)
{std::cout << typeid(T).name() << "::doWork(" << x << ")\n";}
};
struct TypeofLessThan
{
bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
{return lhs->before(*rhs);}
};
struct Manager
{
typedef std::map<const std::type_info*, BaseWorker*, TypeofLessThan> WorkerMap;
~Manager()
{
// Delete all BaseWorkers in workerMap_
WorkerMap::iterator it;
for (it = workerMap_.begin(); it != workerMap_.end(); ++it)
delete it->second;
}
template <class T>
void doSomething(T const& x)
{
WorkerMap::iterator it = workerMap_.find(&typeid(T));
if (it == workerMap_.end())
{
it = workerMap_.insert(
std::make_pair(&typeid(T), new Worker<T>) ).first;
}
Worker<T>* worker = static_cast<Worker<T>*>(it->second);
worker->work(&x);
}
WorkerMap workerMap_;
};
int main()
{
Manager manager;
const int N = 10;
for (int i=0;i<N;i++)
{
manager.doSomething<int>(3);
manager.doSomething<char>('x');
manager.doSomething<float>(3.14);
}
}
map<std::type_info, BaseWorker*> doesn't work because type_info is not copy-constructible. I had do use map<const std::type_info*, BaseWorker*>. I just need to check that typeid(T) is guaranteed to always return the same reference (I think it is).
It doesn't matter whether or not typeid(T) returns the same reference, because I always use type_info::before do to all comparisons.
something like this will work:
struct Base { };
template<class T> struct D : public Base { Manager<T> *ptr; };
...
struct Manager {
...
Base *ptr;
};