// g++ -std=c++14 -Wall -Wextra -Wpedantic -o seany seany.cpp

// Concept-based Relaying Any type
// C++14 Sean Parent-inspired concept-parameterized std::any-like type.
// https://git.rcrnstn.net/rcrnstn/crany


// CXXFLAGS=''


// "concept-model", "runtime concept", "virtual concept" idiom.

// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#t5-combine-generic-and-oo-techniques-to-amplify-their-strengths-not-their-costs
// https://wiki.c2.com/?ExternalPolymorphism
// https://www.dre.vanderbilt.edu/~schmidt/PDF/C++-EP.pdf
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Polymorphic_Value_Types

// Patterns:
// - Adapter pattern
//   - [Wikipedia](https://en.wikipedia.org/wiki/Adapter_pattern)
//   - [Gamma, E. et al. *Design Patterns: Elements of Reusable Object-Oriented Software*, Addison–Wesley, 1995](https://en.wikipedia.org/wiki/Design_Patterns)
// - Decorator pattern
//   - [Wikipedia](https://en.wikipedia.org/wiki/Decorator_pattern)
//   - [Gamma, E. et al. *Design Patterns: Elements of Reusable Object-Oriented Software*, Addison–Wesley, 1995](https://en.wikipedia.org/wiki/Design_Patterns)
// - External polymorphism pattern
//   - [WikiWikiWeb](https://wiki.c2.com/?ExternalPolymorphism)
//   - [Cleeland, C., D. C. Schmidt, and T. Harrison. "External Polymorphism"](https://www.dre.vanderbilt.edu/~schmidt/PDF/C++-EP.pdf)

// Kevlin Henney
//   [Valued Conversions](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.374.2252&rep=rep1&type=pdf)
//     C++ Report 12(7) 2000
// [Sean Parent](https://sean-parent.stlab.cc)
//   [Better Code: Runtime Polymorphism](https://sean-parent.stlab.cc/papers-and-presentations/#better-code-runtime-polymorphism)
//     NDC 2017, [video](https://www.youtube.com/watch?v=QGcVXgEVMJg)
//   [Inheritance Is The Base Class of Evil](https://sean-parent.stlab.cc/papers-and-presentations/#inheritance-is-the-base-class-of-evil)
//     GoingNative 2013, [video](https://www.youtube.com/watch?v=bIhUE5uUFOA)
//   [Value Semantics and Concept-based Polymorphism](https://sean-parent.stlab.cc/papers-and-presentations/#value-semantics-and-concept-based-polymorphism)
//     CppNow 2012, [video](https://www.youtube.com/watch?v=_BpMYeUFXv8)
//   [Concept-Based Runtime Polymorphism](https://stlab.cc/legacy/figures/Boost_poly.pdf)
//      BoostCon 2007
//   [Runtime Concepts](https://stlab.cc/legacy/runtime-concepts.html)
// Louis Dionne
//   Runtime Polymorphism: Back to the Basics
//     [C++Now 2018](https://ldionne.github.io/cppnow-2018-runtime-polymorphism), [video](https://www.youtube.com/watch?v=OtU51Ytfe04)
//     [CppCon 2017](https://ldionne.github.io/cppcon-2017-runtime-polymorphism), [video](https://www.youtube.com/watch?v=gVGtNFg4ay0)
// Zach Laine
//   Pragmatic Type Erasure: Solving OOP Problems w/ Elegant Design Pattern
//     CppCon 2014, [video](https://www.youtube.com/watch?v=0I0FD3N5cgM)
// Klaus Igelberger
//   Breaking Dependencies: Type Erasure - A Design Analysis
//     CppCon 2021, [video](https://www.youtube.com/watch?v=4eeESJQk-mw&t=23m26s)
// Sy Brand
//   Dynamic Polymorphism with Metaclasses and Code Injection
//     CppCon 2020, [video](https://www.youtube.com/watch?v=8c6BAQcYF_E)


// Related projects:
// - Adbobe's [Poly](https://stlab.adobe.com/group__poly__related.html)
// - Facebook's [Folly Poly](https://github.com/facebook/folly/blob/main/folly/docs/Poly.md)
// - Louis Dionne's [Dyno](https://github.com/ldionne/dyno)
//   - https://www.youtube.com/watch?v=OtU51Ytfe04
//   - https://www.youtube.com/watch?v=H8lFldGvt9w
//   - https://www.youtube.com/watch?v=gVGtNFg4ay0


// Improvements:
// - Read the code for `std::any`/`std::bad_any_cast` and steal away :)
// - Make the code slightly uglier to support C++11? Actually, can we go even
//   lower? When was template template arguments introduced?
// - Optimization. Ok, we got everything we want in the proof of concept, look
//   into Small Buffer Optimization? Something like `dyno`'s storage policy?
//   Luckily, `dyno`'s author recommends against futzing with the vtable, so
//   implementing SBO might not be too hard. See also
//   <https://www.youtube.com/watch?v=va9I2qivBOA&t=40m21s>.
// - Both `dyno` and `folly::Poly` uses roughly the same example. Use it too.
// - Introduce another template parameter to `Concept` so that it is possible
//   to use a template specialization as an Adapter?
// - Check if it's possible to supply only `operator->()` in the `Concept` and
//   *all* member functions forwarded, at the cost of using `->` instead of
//   `.`.
// - It would be nice if we could handle (non-owning) pointers as well. Do we?
// - Diagrams with https://plantuml.com.

// SOLVED:
// - SOLVED(Using `std::is_void` seems reasonable) `CranySelf` is leaked into
//   the global namespace.  Encapsulation. The technique of putting it in a
//   template specialization of `Crany` so it can be inherited by the general
//   `Crany` is harder when the template argument itself is a template (What
//   should be the specialization? The usual `void` doesn't work.
//   `std::type_identity` was not introduced until C++20.).
// - SOLVED(we can just provide a templated free function that forwards to its
//   argument and template speclialize that on `Shapes`) Sean Parent's
//   implementation delegates to free functions in the `Model` but we do not.
//   Doing so would allow us to implement e.g. `area(Shapes const &)` without
//   having to do anything more complicated than `using Shapes =
//   std::vector<Shape>`, which would be nice. Maybe make `Crany::Model`
//   `public` and allow the client concept to specialize on it?
// - SOLVED, AMAZING :D Do away with the user having to specify the
//   `BaseConcept` twice, once generally and once as a `void` specialization.
//   This would cut user boilerplate down by a factor of 2, but seems very hard
//   to do. Maybe possible with some macro magic?

// Document:
// - Document requirements of wrapped types. Is `CopyConstructible` enough?
// - Support e.g. `Square` member functions returning `Square &`. Actually,
//   this is not possible (Square should return `Square &`, but there is no
//   common base class that `Crany` can return. Document this fact.
// - Document (test first) what a user has to do if they want their `Concept`
//   to include a member function called `self`. Actually, this is not possible
//   without making `self` a `static` member and take the this pointer as an
//   argument. This would make the API uglier, just don't support it. Document
//   this fact.
// - If a user forgets to mark a member function `virtual` in the `Concept`,
//   the program segfaults :S Would be nice to catch that at compile time. It's
//   probably caused by infinite recursion in `Concept<ConceptSelf>` (verify!).
//   We probably can't do much about it, but at least document.


// TODO: Compare compiler output (as performance indicator) for both this
// parameterized version and a version that hardcodes the member functions.


#include <memory>
#include <type_traits>


template<template<typename> class = std::is_void>
class Crany;

/// CranySelf
template<>
class Crany<>
{
    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_; }
    };
};

/// Crany
template<template<typename> class BaseConcept>
class Crany final : public BaseConcept<Crany<>::CranySelf<Crany<BaseConcept>>>
{
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<BaseConcept>>;

    /// ConceptSelf
    struct ConceptSelf
    {
    protected:
        auto       & self()       { return *(BaseConcept<ConceptSelf>       *)this; }
        auto const & self() const { return *(BaseConcept<ConceptSelf> const *)this; }
    };

    /// Concept
    struct Concept : BaseConcept<ConceptSelf>
    {
        virtual ~Concept() = default;
        virtual Concept * clone() const = 0;
    };

    /// ModelSelf
    template<typename T>
    struct ModelSelf : Concept
    {
    protected:
        auto       & self()       { return ((T       *)this)->self_; }
        auto const & self() const { return ((T const *)this)->self_; }
    };

    /// Model
    template<typename Self>
    struct Model final : BaseConcept<ModelSelf<Model<Self>>>
    {
        Model(Self self) : self_{std::move(self)} {}
        Concept * clone() const { return new Model<Self>(*this); }
        Self self_;
    };

    /// Self
    std::unique_ptr<Concept> 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
{
    template<template<typename> class> friend class Crany;
};

/// crany_cast
template<typename T, typename Crany>
T const * crany_cast(Crany const * crany) noexcept
{
    if (!crany)
        return nullptr;
    if(auto * model = dynamic_cast<typename Crany::Model<T> *>(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
}


#include <cmath>
#include <iostream>
#include <vector>


auto constexpr pi = std::acos(-1);


template<typename Self>
struct ShapeConcept : Self
{
    virtual float area() const       { return self().area();       }
    virtual void  scale(float value) { return self().scale(value); }
private:
    using Self::self;
};

using Shape = Crany<ShapeConcept>;
using Shapes = std::vector<Shape>;


struct Square
{
    float side;
    float area() const       { return side*side; }
    void  scale(float scale) { side *= scale;    }
};

struct Circle
{
    float radius;
    float area() const       { return pi*radius*radius; }
    void  scale(float scale) { radius *= scale;         }
};


int main()
{
    auto shapes = Shapes{
        Square{2},
        Circle{3},
    };
    shapes.push_back(shapes.front());
    shapes.back().scale(2);
    // std::cout << shapes.front().self() << std::endl;

    std::cout << std::boolalpha;
    for (auto const & shape : shapes)
        std::cout
            << shape.area()
            << " "
            << !!crany_cast<Square>(&shape)
            << "\n";
}