#include <memory>
#include <iostream>

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

//
//  CRTP base class of pin
//

/*
    avoiding virtual call (static polymorphism)
*/

template <typename Derived>
struct PinBase
{
    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()
    {
        std::cout << "created AVR_Pin" << std::endl;
    }

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

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

    bool getImp()
    {
        return true;
    }

    void avr_stuff()
    {
        std::cout << "AVR specific stuff" << std::endl;
    }
};

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);
}

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

    pin1.STM32_stuff();

    pin2.avr_stuff();

    foo(pin1);
    foo(pin2);

    baa(pin1);

    while(1);

    return 0;
}