Question:
I have seen articles in English that talk about this, as I have come to understand they are used for encapsulation in C. Can someone explain what exactly is an incomplete type and an opaque pointer? And what uses do they have in C?
Answer:
An incomplete type is as its name implies, a type without definition, which is complemented by opaque pointers, let's consider the following example:
fecha.h
typedef struct date date_t;
unsigned get_hours(date_t *dt);
//... Resto de funcionalidades
fecha.c
typedef struct date {
// implementación de la estructura.
} date_t;
You have an opaque implementation by providing only the declaration of the date_t
data date_t
, allowing the user of your library only a pointer to the structure and a set of functions to give it behavior.
Everything is defined in the code file that will be compiled to deliver the binary to whoever uses it.
These implementations are used to keep the code as hidden as possible from end users, so they don't know the content of the pointer internally.
At the end:
- Opaque pointer: It is a pointer to a structure that we do not know (4 or 8 bytes) that we do not know what they contain or their real size.
- Incomplete type: Type that has only been declared but not defined in the eyes of the end user.
A practical example:
/* date_t.h */
#ifndef DATE_T_HEADER
#define DATE_T_HEADER
/* declaración opaca */
typedef struct date date_t;
/* algunos métodos */
date_t *new_date(void);
unsigned get_hours(date_t *dt);
void set_hours(date_t *dt, unsigned t);
unsigned get_difference_seconds(date_t *dt1, date_t *dt2);
void delete_date(date_t *dt);
#endif /* DATE_T_HEADER */
/* date_t.c */
#include <stdlib.h>
#include <stdio.h>
#include "date_t.h"
/* Definición de la estructura */
typedef struct date {
size_t time;
char holamundo;
} date_t; /* FAIL PADDING, sizeof esta estructura es 16 u 8 ... */
date_t *new_date(void) {
date_t *ptr = malloc(sizeof(struct date));
return ptr;
}
unsigned get_hours(date_t *dt) {
if (dt) return dt->time;
return 0;
}
void set_hours(date_t *dt, unsigned t) {
if (dt) dt->time = t;
}
unsigned get_difference_seconds(date_t *dt1, date_t *dt2) {
if (dt1 && dt2) return dt1->time - dt2->time;
return 0;
}
void delete_date(date_t *dt) {
if (dt) {
free(dt);
dt = NULL;
}
}
/* test.c */
#include <stdio.h>
#include "date_t.h"
int main(void) {
date_t *ptrdate = new_date();
printf("Nueva fecha\nAjustando a 55 las horas...\n");
set_hours(ptrdate, 55);
if (get_hours(ptrdate) == 55)
printf("En efecto es 55.\n");
date_t *se = new_date();
printf("Creada nueva fecha...\n");
set_hours(se, 10);
printf("Diferencias: %u\n", get_difference_seconds(ptrdate, se));
delete_date(ptrdate); delete_date(se);
}
Note that it has been upside down so it may not work at all (Pending until I find my computer) .
Try the following command line to get result:
> gcc -o date.o -c -std=c11 date.c
> gcc -o test.o -c -std=c11 test.c
> gcc -o test *.o
It should return the following result:
Nueva fecha
Ajustando a 55 las horas...
En efecto es 55.
Creada nueva fecha...
Diferencias: 45
SN 1 : -std=c11
because you never have to lose style.
Finally, the main advantages of implementing this type of opacity in C is that you limit the amount of changes that users are able to make to your structure, which has a collateral effect, since not everyone wants to do things your way. , but the implementation is useful when making software to sell.
And like everything in C, it is not good to abuse this type of methodology.
1 : Side note.