Below is the (very) stripped down code of a implementation.
The library code is as follows:
#pragma once
#include <iostream>
#include <map>
#include <string>
// Necessary as interface and due to QObject Macro.
class Base
{
public:
Base( const std::string& name ) : name_( name )
{
}
virtual ~Base(){}
const std::string& name()
{
return name_;
}
private:
std::string name_;
};
template < typename Derived, typename ObjectType >
class TemplatedBase : public Base
{
public:
TemplatedBase( const std::string& name ) : Base( name )
{
}
ObjectType object()
{
return object_;
}
ObjectType object_;
};
class DerivedA : public TemplatedBase< DerivedA, int >
{
public:
DerivedA( const std::string& name ) : TemplatedBase< DerivedA, int >( name )
{
}
};
class DerivedB : public TemplatedBase< DerivedB, float >
{
public:
DerivedB( const std::string& name ) : TemplatedBase< DerivedB, float >( name )
{
}
};
class Container
{
public:
template < typename T >
void addToMap( T& map_object )
{
const std::string name = map_object.name();
// ASSERT( map_.find( name ) == map_.end() );
map_.emplace( std::make_pair( name, &map_object ) );
}
template < typename T >
auto getObject( std::string name ) -> decltype( ( ( T* )nullptr )->object() )
{
auto search = map_.find( name );
// How can this dynamic_cast be avoided?
T* ptr = dynamic_cast< T* >( search->second );
// ASSERT( ptr == nullptr );
return ptr->object();
}
std::map< std::string, Base* > map_;
};
With an example usage of:
int main( int argc, char* argv[] )
{
Container container;
DerivedA a( "Name_A" );
DerivedB b( "Name_B" );
container.addToMap( a );
container.addToMap( b );
// How can I avoid to specify the type in the map as template?
auto object_a = container.getObject< DerivedA >( "Name_A" );
auto object_b = container.getObject< DerivedB >( "Name_B" );
}
Some explanation for the code:
- The Base Class is necessary because the
Q_OBJECT
Macro requires a non-template class as well as for the interface. - The templated Base class has some sort of lazy copy implemented, as the objects stored are relativly large in the real application.
- The Container is made available for an arbitrary number of threads and the lazy copy of the TemplatedBase Class is handling the thread safety.
- The Container is used in a context that doesn't know (nor need to know) the Derived Classes themself.
While the code itself is working fine, I am looking for a different design that makes the dynamic_cast and the type specification during readout unnecessary.
I have tried several types of type erasure (boost::type_erasure, boost::variant) and different container types. However I always ran into the problem of different return types of the object()
function.