Вы можете объявить саму структуру как угодно, если вы скрываете ее за частной инкапсуляцией. Это будет один из вариантов: разрешить доступ к структуре можно только через сеттеры / геттеры.
Предположим, вы определили две структуры следующим образом:
typedef struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint16_t d;
uint8_t e;
} foo_write_t;
typedef struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint16_t d;
uint8_t e;
uint16_t f;
uint32_t g;
uint32_t h;
} foo_read_t;
Затем вы можете создать новый ADT, содержащий оба элемента с перекрывающимися элементами. A union
, например:
union foo_t
{
foo_write_t write;
foo_read_t read;
struct // C11 anonymous struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint16_t d;
uint8_t e;
uint16_t f;
uint32_t g;
uint32_t h;
};
};
Выше теперь есть 3 разных способа доступа к одной и той же памяти. foo.a
, foo.write.a
или foo.read.a
. Однако foo.write
не хватает f, g, h. Если мы добавим приватную инкапсуляцию, мы также можем заблокировать прямой доступ к foo.a
.
В C частная инкапсуляция выполняется через непрозрачный тип , заголовок "foo.h" становится примерно таким:
// foo.h
typedef union foo_t foo_t;
typedef struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint16_t d;
uint8_t e;
} foo_write_t;
typedef struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint16_t d;
uint8_t e;
uint16_t f;
uint32_t g;
uint32_t h;
} foo_read_t;
foo_t* foo_init(
uint8_t a,
uint8_t b,
uint8_t c,
uint16_t d,
uint8_t e,
uint16_t f,
uint32_t g,
uint32_t h);
void foo_delete (foo_t* foo);
и соответствующий файл c:
// foo.c
#include "foo.h"
union foo_t
{
foo_write_t write;
foo_read_t read;
struct
{
uint8_t a;
uint8_t b;
uint8_t c;
uint16_t d;
uint8_t e;
uint16_t f;
uint32_t g;
uint32_t h;
};
};
foo_t* foo_init(
uint8_t a,
uint8_t b,
uint8_t c,
uint16_t d,
uint8_t e,
uint16_t f,
uint32_t g,
uint32_t h)
{
foo_t* foo = malloc(sizeof *foo);
if(foo==NULL)
{
return NULL;
}
foo->a = a;
foo->b = b;
foo->c = c;
foo->d = d;
foo->e = e;
foo->f = f;
foo->g = g;
foo->h = h;
return foo;
}
void foo_delete (foo_t* foo)
{
free(foo);
}
Тогда вы можете предоставить доступ через сеттеры / геттеры. Либо разрешив только печатные копии, что-то вроде этого:
void foo_write (foo_t* foo, const foo_write_t* foo_w)
{
memcpy(foo, foo_w, sizeof foo->write);
}
void foo_read (const foo_t* foo, foo_read_t* foo_r)
{
memcpy(foo_r, foo, sizeof foo->read);
}
или разрешив вызывающей стороне записать непосредственно в ADT, выставив и ретранслировав адрес соответствующей части структуры:
foo_write_t* foo_get_write (foo_t* foo)
{
return &foo->write;
}
const foo_read_t* foo_get_read (foo_t* foo)
{
return &foo->read;
}