include/crany.hpp
1c2b77f6
 #ifndef CRANY_HPP_
 #define CRANY_HPP_
 
 
 #include <memory>
 #include <type_traits>
 #include <cassert>
 
 
 template<template<typename> class = std::is_void>
 class Crany;
 
 /// CranySelf
 template<>
 class Crany<>
 {
 private:
     template<template<typename> class> friend class Crany;
     template<typename T>
     struct CranySelf
     {
     protected:
         auto       & self()       { return *((T       *)this)->self_; }
         auto const & self() const { return *((T const *)this)->self_; }
         virtual ~CranySelf() = default;
     };
 };
 
 /// Crany
 template<template<typename> class Concept>
 class Crany final : public Concept<Crany<>::CranySelf<Crany<Concept>>>
 {
 public:
 
     /// Special member functions
     template<typename Self>
     Crany(Self self) : self_{new Model<Self>(std::move(self))} {}
     Crany(Crany const & other) : self_(other.self_->clone()) {}
     Crany & operator=(Crany const & other) { return *this = Crany(other); }
     Crany(Crany && other) = default;
     Crany & operator=(Crany && other) = default;
 
 private:
 
     /// CranySelf
     friend Crany<>::CranySelf<Crany<Concept>>;
 
     /// ConceptSelf
     struct ConceptSelf
     {
     protected:
         auto       & self()       { assert(!"Crany Concept non-virtual member function called"); return *(Concept<ConceptSelf>       *)this; }
         auto const & self() const { assert(!"Crany Concept non-virtual member function called"); return *(Concept<ConceptSelf> const *)this; }
         virtual ~ConceptSelf() = default;
     };
 
     /// CloneConcept
     struct CloneConcept : Concept<ConceptSelf>
     {
         virtual CloneConcept * clone() const = 0;
     };
 
     /// ModelSelf
     template<typename T>
     struct ModelSelf : CloneConcept
     {
     protected:
         auto       & self()       { return ((T       *)this)->self_; }
         auto const & self() const { return ((T const *)this)->self_; }
     };
 
     /// Model
     template<typename Self>
     struct Model final : Concept<ModelSelf<Model<Self>>>
     {
         Model(Self self) : self_{std::move(self)} {}
         CloneConcept * clone() const { return new Model<Self>(*this); }
         Self self_;
     };
 
     /// Self
     std::unique_ptr<CloneConcept> self_;
 
     /// crany_cast
     template<typename T, typename Crany> friend T const * crany_cast(Crany const *  crany) noexcept;
     template<typename T, typename Crany> friend T       * crany_cast(Crany       *  crany) noexcept;
     template<typename T, typename Crany> friend T         crany_cast(Crany const &  crany);
     template<typename T, typename Crany> friend T         crany_cast(Crany       &  crany);
     template<typename T, typename Crany> friend T         crany_cast(Crany       && crany);
 };
 
 /// bad_crany_cast
 class bad_crany_cast : public std::bad_cast
 {
 };
 
 /// crany_cast
 template<typename T, typename Crany>
 T const * crany_cast(Crany const * crany) noexcept
 {
     if (!crany)
         return nullptr;
     using Model = typename Crany::Model<T>;
     if (auto * model = dynamic_cast<Model *>(crany->self_.get()))
         return &model->self_;
     return nullptr;
 }
 template<typename T, typename Crany>
 T * crany_cast(Crany * crany) noexcept
 {
     return const_cast<T *>(*const_cast<T const *>(crany));
 }
 template<typename T, typename Crany>
 T crany_cast(Crany const & crany)
 {
     using U = std::remove_cv_t<std::remove_reference_t<T>>;
     if (auto p = crany_cast<U>(&crany))
         return static_cast<T>(*p);
     throw bad_crany_cast(); // TODO
 }
 template<typename T, typename Crany>
 T crany_cast(Crany & crany)
 {
     using U = std::remove_cv_t<std::remove_reference_t<T>>;
     if (auto p = crany_cast<U>(&crany))
         return static_cast<T>(*p);
     throw bad_crany_cast(); // TODO
 }
 template<typename T, typename Crany>
 T crany_cast(Crany && crany)
 {
     using U = std::remove_cv_t<std::remove_reference_t<T>>;
     if (auto p = crany_cast<U>(&crany))
         return static_cast<T>(std::move(*p));
     throw bad_crany_cast(); // TODO
 }
 
 
 #endif // CRANY_HPP_