#include <iostream>
#include <memory>
#include <typeindex>
class any_ptr
{
struct WrapperBase
{
virtual ~WrapperBase(){}
};
template< typename T >
struct Wrapper : WrapperBase, T
{
template<typename ... Args>
Wrapper( Args&&... args ):
T( std::forward<Args>(args)... ) {}
};
struct PointerBase : WrapperBase
{
std::type_index typeIndex;
PointerBase( std::type_index ti ):
typeIndex{ti} {}
};
template< typename T >
struct ScalarWrapper : WrapperBase
{
T scalar;
template<typename Arg>
ScalarWrapper( Arg&& arg ):
scalar( std::forward<Arg>(arg) ) {}
};
std::unique_ptr<WrapperBase> mPtr;
any_ptr( std::unique_ptr<WrapperBase> ptr ):
mPtr{ std::move(ptr) } {}
template< typename T >
T* _access( std::false_type, std::false_type ) const
{
return dynamic_cast<T*>(mPtr.get());
}
template< typename T >
T* _access( std::true_type, std::false_type ) const
{
auto ptr = dynamic_cast<ScalarWrapper<T>*>(mPtr.get());
return ptr ? &ptr->scalar : nullptr ;
}
template< typename T >
T* _access( std::true_type, std::true_type ) const
{
using pointee = typename std::remove_pointer<T>::type;
auto ptr = dynamic_cast<PointerBase*>(mPtr.get());
if( ptr
&& ptr->typeIndex <= typeid(pointee) ) // Dieser Typ ist eine Basisklasse oder ein größerer Skalar o.ä.
return &static_cast<ScalarWrapper<T>*>(dynamic_cast<void*>(ptr))->scalar;
return _access<T>( std::true_type{}, std::false_type{} );
}
template< typename T,
typename ... Args >
static any_ptr _make_any_ptr( std::false_type, Args&&... args )
{
return std::unique_ptr<WrapperBase>{ new Wrapper<T>{ std::forward<Args>(args)... } };
}
template< typename T >
static any_ptr _make_any_ptr( std::true_type, T arg )
{
return std::unique_ptr<WrapperBase>{ new ScalarWrapper<T>{arg} };
}
public:
template< typename T >
T* access()
{ return _access<T>( typename std::is_scalar<T>::type{}, typename std::is_pointer<T>::type{} ); }
template< typename T >
T const* access() const
{ return const_cast<any_ptr*>(this)->access<T>(); }
template< typename T,
typename ... Args >
friend any_ptr make_any_ptr( Args&&... args )
{
return _make_any_ptr<T>( typename std::is_scalar<T>::type{}, std::forward<Args>(args)... );
}
};
template< typename T >
struct any_ptr::ScalarWrapper<T*> : any_ptr::PointerBase
{
T* scalar;
template<typename Arg>
ScalarWrapper( Arg&& arg ):
PointerBase{ typeid(T) },
scalar( std::forward<Arg>(arg) ) {}
};
////////////////////////////////////////////////////////////////////////////////
template <class T>
void inspect(any_ptr& p, void (&fun)(T))
{
using actual_t = typename std::remove_reference<T>::type;
if( auto t = p.access<actual_t>() )
fun(*t);
}
////////////////////////////////////////////////////////////////////////////////
class printable
{
public:
virtual void print() const = 0;
};
////////////////////////////////////////////////////////////////////////////////
#include <string>
using namespace std;
class foo : public printable
{
public:
foo(string const& s) : m_s(s) {}
virtual void print() const
{
cout << "foo: " << m_s << "\n";
}
private:
string m_s;
};
class bar : public printable
{
public:
bar(int i) : m_i(i) {}
virtual void print() const
{
cout << "bar: " << m_i << "\n";
}
private:
int m_i;
};
////////////////////////////////////////////////////////////////////////////////
void print_printable(printable& p)
{
p.print();
}
////////////////////////////////////////////////////////////////////////////////
int main()
{
bar b = 42;
auto any = make_any_ptr<bar*>(&b);
auto b_ptr = any.access<printable*>();
if( b_ptr )
(*b_ptr)->print();
short i = 42;
auto any2 = make_any_ptr<short*>(&i);
auto i_ptr = any2.access<int*>();
if( i_ptr )
std::cout << **i_ptr;
}