Search code examples
coopstructavravr-gcc

(pseudo) OOP in C get struct object from its function pointer


I am trying to create my own small AVR library. I have idea to use pseudo OOP in code, and currently i have "classes" defined as structs. I am considering if it's possible to implement something like "this" keyword. I want to get struct object inside function assigned to function pointer which is member of struct.

My code:

#define __class__                   struct
#define __inner_object__            struct

#define ALIAS(cls, stc)             typedef __class__ cls stc
typedef uint8_t     reg8_t;

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({                      \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

typedef enum {
    PIN_DIR_INPUT  = 0,
    PIN_DIR_OUTPUT = 1
} pin_direction_t;

typedef struct {
    const reg8_t        m_pin;
    const reg8_t        m_ddr;
    const reg8_t        m_port;
    const u1            m_bit;

    void                (*setDirection)(pin_direction_t);
} pin_t;

void __setDirection(pin_direction_t direction)
{
    // how to get struct object here?
    pin_t* pin = container_of(__setDirection, pin_t, setDirection);
    //uint8_t reg = reg_read(pin->m_ddr);
    //if (direction == PIN_DIR_OUTPUT)
        //reg_write(pin->m_ddr, reg | (1uL << pin->m_bit));
    //else
        //reg_write(pin->m_ddr, reg & ~(1uL << pin->m_bit));
}

__class__ at_mega8
{
    __inner_object__
    {
        const reg8_t    REG_PIND;
        const reg8_t    REG_DDRD;
        const reg8_t    REG_PORTD;
        __inner_object__
        {
            const pin_t PIN_D7;
            const pin_t PIN_D6;
            const pin_t PIN_D5;
            const pin_t PIN_D4;
            const pin_t PIN_D3;
            const pin_t PIN_D2;
            const pin_t PIN_D1;
            const pin_t PIN_D0;
        } Pins;
    } PortD;

    __inner_object__
    {
        const reg8_t    REG_PINC;
        const reg8_t    REG_DDRC;
        const reg8_t    REG_PORTC;
        __inner_object__
        {
            const pin_t PIN_C6;
            const pin_t PIN_C5;
            const pin_t PIN_C4;
            const pin_t PIN_C3;
            const pin_t PIN_C2;
            const pin_t PIN_C1;
            const pin_t PIN_C0;
        } Pins;
    } PortC;

    __inner_object__
    {
        const reg8_t    REG_PINB;
        const reg8_t    REG_DDRB;
        const reg8_t    REG_PORTB;
        __inner_object__
        {
            const pin_t PIN_B7;
            const pin_t PIN_B6;
            const pin_t PIN_B5;
            const pin_t PIN_B4;
            const pin_t PIN_B3;
            const pin_t PIN_B2;
            const pin_t PIN_B1;
            const pin_t PIN_B0;
        } Pins;
    } PortB;
};

ALIAS(at_mega8, at_mega8_t);

#define M8_PIND     0x10
#define M8_DDRD     0x11
#define M8_PORTD    0x12

#define M8_PINC     0x13
#define M8_DDRC     0x14
#define M8_PORTC    0x15

#define M8_PINB     0x16
#define M8_DDRB     0x17
#define M8_PORTB    0x18

const at_mega8_t AtMega8 = {
    {
        M8_PIND, M8_DDRD, M8_PORTD,
        {
            { .m_bit = 7, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 6, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 5, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 4, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 3, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 2, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 1, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
            { .m_bit = 0, .m_pin = M8_PIND, .m_ddr = M8_DDRD, .m_port = M8_PORTD, .setDirection = __setDirection },
        }
    },
    {
        M8_PINC, M8_DDRC, M8_PORTC,
        {
            { .m_bit = 6, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
            { .m_bit = 5, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
            { .m_bit = 4, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
            { .m_bit = 3, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
            { .m_bit = 2, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
            { .m_bit = 1, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
            { .m_bit = 0, .m_pin = M8_PINC, .m_ddr = M8_DDRC, .m_port = M8_PORTC, .setDirection = __setDirection },
        }
    },
    {
        M8_PINB, M8_DDRB, M8_PORTB,
        {
            { .m_bit = 7, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 6, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 5, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 4, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 3, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 2, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 1, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
            { .m_bit = 0, .m_pin = M8_PINB, .m_ddr = M8_DDRB, .m_port = M8_PORTB, .setDirection = __setDirection },
        }
    },
};

I could use this code in below example:

hd44780.pinout.pinRS = &AtMega8.PortB.Pins.PIN_B1;
hd44780.init();
// and somewhere else in hd44780:
// rsPin.setDirection(PIN_DIR_OUTPUT);

I know there is container_of macro which allows to get container object by its member. But when i am trying to use this macro i am getting error:

error: dereferencing pointer to incomplete type

This error points to container_of macro definition.

So, i have two questions. Is it possible to get struct object inside assigned function on avr-gcc? If no, is it possible to do this on gcc (windows)? Of course i could pass struct object to function as paramater but this would be ugly


Solution

  • Short answer, no - to do what you want with container_of you'd need the address of the function pointer in your specific object, not the address of the function it points to, and to get that you'd need some kind of pointer to the struct or to one of its members passed into the function.

    If you look at what, say, C++, is doing under the hood, it's also basically passing a pointer to a struct. You just don't see it happening, because the language hides it from you. There's no way around the fact that somehow the function has to be told which object you want it to work with - it can't know this by magic.