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
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.