#include <iostream>
#include <memory>
template<typename T>
using require_scalar = typename std::enable_if<std::is_arithmetic<T>::value>::type;
template<typename T>
using require_not_scalar = typename std::enable_if<not std::is_arithmetic<T>::value>::type;
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)... ) {}
};
template< typename T >
struct ScalarWrapper : WrapperBase
{
T scalar;
template<typename ... Args>
ScalarWrapper( Args&&... args ):
scalar( std::forward<Args>(args)... ) {}
};
std::unique_ptr<WrapperBase> mPtr;
any_ptr( std::unique_ptr<WrapperBase> ptr ):
mPtr{ std::move(ptr) } {}
template< typename T >
T* _access( std::false_type ) const
{
return dynamic_cast<T*>(mPtr.get());
}
template< typename T >
T* _access( std::true_type ) const
{
auto ptr = dynamic_cast<ScalarWrapper<T>*>(mPtr.get());
return ptr ? &ptr->scalar : nullptr ;
}
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_arithmetic<T>::type{} ); }
template< typename T >
T const* access() const
{ return _access<T>( typename std::is_arithmetic<T>::type{} ); }
template< typename T,
typename ... Args >
friend any_ptr make_any_ptr( Args&&... args )
{
return _make_any_ptr<T>( typename std::is_arithmetic<T>::type{}, std::forward<Args>(args)... );
}
};
////////////////////////////////////////////////////////////////////////////////
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()
{
auto f = make_any_ptr<foo>("foo");
auto b = make_any_ptr<bar>(42);
auto c = make_any_ptr<int>(42);
inspect(f, print_printable);
inspect(b, print_printable);
auto ptr = c.access<int>();
std::cout << *ptr;
}