#include <memory>
#include <iostream>

// https://www.modernescpp.com/index.php/c-is-still-lazy

//
//  CRTP base class of pin
//

/*
    avoiding virtual call (static polymorphism)
*/

struct IPin
{
    void set(bool logic);
    void toggle();
    bool get();
};

template <typename Derived>
struct PinBase : public IPin
{
    void set(bool logic)
    {
        static_cast<Derived*>(this)->setImp(logic);
    }

    void toggle()
    {
        static_cast<Derived*>(this)->toggleImp();
    }

    bool get(void)
    {
        return static_cast<Derived*>(this)->getImp();
    }

    private:

    //
    // base implementations
    //

    void setImp()
    {
        std::cout << "base implementation of set()!" << std::endl;
    }

    void toggleImp()
    {
        std::cout << "base implementation of toggle()!" << std::endl;
    }

    bool getImp(void)
    {
        std::cout << "base implementation of get()!" << std::endl;
        return true;
    }
};

//
//  implementations
//

struct STM32_Pin : PinBase<STM32_Pin>
{
    STM32_Pin()
    {
        //std::cout << "created STM32_Pin" << std::endl;
    }

    void setImp(bool logic)
    {
        //std::cout << "stm32 pin set to " << logic << std::endl;
    }

    void toggleImp()
    {
        //std::cout << "toggled stm32 pin" << std::endl;
    }

    bool getImp()
    {
        return true;
    }


    void STM32_stuff()
    {
        //std::cout << "STM_32 specific stuff" << std::endl;
    }
};

struct AVR_Pin : PinBase<AVR_Pin>
{
    AVR_Pin()
    {
        a = 3;
        //std::cout << "created AVR_Pin" << std::endl;
    }

    void setImp(bool logic)
    {
        //std::cout << "AVR pin set to " << logic << std::endl;
    }

    void toggleImp()
    {
        a = 5;
        //std::cout << "toggled AVR pin" << std::endl;
    }

    bool getImp()
    {
        return true;
    }

    void avr_stuff()
    {
        a = 10;
        //std::cout << "AVR specific stuff" << std::endl;
    }
    private:

    int a;
};

template<typename T>
void foo(T& base)
{
    base.set(true);
    base.set(false);
    base.toggle();
}

template<typename T>
void baa(PinBase<T>& pin)
{
    pin.toggle();
    //foo(pin);
}

#if 0
template <typename T>
struct Driver
{
    Driver(PinBase<T>& pin) : pin(pin)
    {
        
    }
    
    void doSomething()
    {
        pin.toggle();
    }
    
    private:
    
    PinBase<T> pin;
};
#endif

struct Driver
{
    Driver(IPin& pin) : pin(pin)
    {
        
    }
    
    void doSomething()
    {
        pin.toggle();
    }
    
    private:
    
    IPin& pin;
};

int main(void)
{
    //STM32_Pin pin1;
    AVR_Pin pin2;


    //pin1.STM32_stuff();

    pin2.avr_stuff();
#if 0
    foo(pin1);
    foo(pin2);

    baa(pin1);
    #endif
    
    
    Driver drv(pin2);
    
    drv.doSomething();
    

    return 0;
}