O documento discute a modularização de código C, apresentando as vantagens da modularização, regras para modularização e exemplos de módulos como "rectangle", "vector" e "framebuffer". O apresentador explica como estruturar módulos em C usando interfaces, implementações e dados privados para manter o código organizado e desacoplado.
1. Click to add Text
Globalcode – Open4education
C/C++ – Modularização de Código C
Felipe de Andrade Neves Lavratti
Me. Eng.º Eletricista desenvolvedor de software
para Sistemas Embarcados
9 de agosto de 2014 – São Paulo – SP
3. Globalcode – Open4education
Principal bibliografia
Capítulo 11:
SOLID em C,
mas sem OOP;
Módulos mais complexos
e completos do que os
propostos;
9 ago 2014 3 / 44Modularização de Código C
4. Globalcode – Open4education
Agenda
Introdução
Porque modularizar
Exemplo
Quatro regras da modularização proposta
A composição do módulo
Usando o módulo “rectangle”
Demais tipos de módulos propostos
Módulo “vector”
Módulo “framebuffer”
Usando o módulo “vector” e “framebuffer”
9 ago 2014 4 / 44Modularização de Código C
5. Globalcode – Open4education
Modularizar é uma metodologia de
arquitetura de software.
O principal objetivo é manter o código
organizado, legível e desacoplado.
A modularização tem como sua principal
regra o SRP (Single Responsibility Principle)
e dependência em abstrações.
9 ago 2014 5 / 44Modularização de Código C
6. Globalcode – Open4education
Não é OOP porque não suporta herança
e polimorfismo facilmente.
Mas é OBP, programação
baseada em objetos.
9 ago 2014 6 / 44Modularização de Código C
8. Globalcode – Open4education
Porque modularizar?
Por que permite a criação de excelentes
arquiteturas:
Design patterns* podem ser aplicados
Cria alicerces para boas práticas de programação:
Como, testes unitários, eliminação de código repetido,
abstração por interfaces, SOLID, incremento na
semântica, etc.
*variações ou simplificações de design patterns ou completos se
implementar OOP manualmente.
9 ago 2014 8 / 44Modularização de Código C
9. Globalcode – Open4education
Exemplo
A seguir função retirada da firmware de um produto real onde
a modularidade não foi empregada.
Depois um exemplo de como ficaria a mesma função se usada
a metodologia de modularização proposta.
9 ago 2014 9 / 44Modularização de Código C
10. Globalcode – Open4education
Exemplo: sem modularização.
Trecho de código
repetido ao longo da
firmware. Dependência em
implementação
Semântica obscura
9 ago 2014 10 / 44Modularização de Código C
11. Globalcode – Open4education
Exemplo: com modularização.
Dependência em
abstração
Semântica
incrementada
9 ago 2014 11 / 44Modularização de Código C
13. Globalcode – Open4education
Regras
Single Responsibility Princple (SRP)
Um módulo deve fazer uma coisa, e fazê-la bem.
Dependência em abstração
Código deve depender de uma funcionalidade, e não de
uma implementação.
Nomenclatura descritiva
Se um módulo e suas funções são bem nomeados, sua
funcionalidade fica clara.
Interface é documentação
Se todos os itens anteriores são obedecidos, então a
interface pode ser a única documentação necessária.
9 ago 2014 13 / 44Modularização de Código C
15. Globalcode – Open4education
INTERFACE
IMPLEMENTAÇÂO
DADOS
Composição do Módulo
Implementação (arquivo .c)
Implementação das
funções, definição dos
tipos privados,
comentários sobre a
implementação.
Dados (struct)
É o tipo do módulo,
quando existir, deve
ser sempre privado.
Contém todos os
dados relevantes,
como instâncias,
buffers, etc.
Interface (arquivo .h)
Funções de acesso,
definições de tipos e
comentários sobre a
abstração.
9 ago 2014 15 / 44Modularização de Código C
16. Globalcode – Open4education
Composição: Dados
Arquivo rectangle.c – projeto marsh – commit 7b4986a
https://github.com/felipe-lavratti/marsh/blob/7b4986ad0a9968d6ce140f8f46b4ac8cb16d4013/marsh/src/rectangle.c
#include “rectangle.h”
...
struct s_rectangle_instance
{
color_t fill_color;
bool is_filled;
color_t border_color;
dim_t border_tickness;
bool has_border;
dim_t corner_radius;
...
};
...
O tipo é definido por uma struct,
seus membros são privados, não
podem ser acessados pelos
clientes diretamente.
todos os dados necessários para
a operação do módulo ficam
contidos nessa struct,
9 ago 2014 16 / 44Modularização de Código C
18. Globalcode – Open4education
O módulo: Interface
#ifndef WIDGET_TREE_H_
#define WIDGET_TREE_H_
...
/* This method delete a widget, its creator, and all tree behind it,
* including the creator of each node.
* Ownership of obj and its children is transferred */
void widget_tree_delete(widget_t * obj);
...
#endif
Arquivo widget_tree.h – projeto marsh – commit 7b4986a
https://github.com/felipe-lavratti/marsh/blob/7b4986ad0a9968d6ce140f8f46b4ac8cb16d4013/marsh/src/widget_tree.h
Na interface comentários relacionados a
abstração, como:
“o que faz” (não “como faz”);
o que é esperado dos parâmetros;
se a posse de algum tipo é transferida;
etc.
9 ago 2014 18 / 44Modularização de Código C
19. Globalcode – Open4education
Comp.: Implementação
Arquivo rectangle.c – projeto marsh – commit 7b4986a
https://github.com/felipe-lavratti/marsh/blob/7b4986ad0a9968d6ce140f8f46b4ac8cb16d4013/marsh/src/rectangle.c
#include “rectangle.h”
...
rectangle_t * rectangle_new(widget_t * parent)
{
rectangle_t * obj = (rectangle_t *) calloc(1, sizeof(struct
s_rectangle_instance));
MEMORY_ALLOC_CHECK_RETURN(obj, NULL);
...
return obj;
}
void rectangle_delete(rectangle_t * const obj)
{
PTR_CHECK(obj, "rectangle");
my_log_delete(obj->log);
widget_delete_instance_only(obj->glyph);
free(obj);
}
...
Sem globais nem variáveis static,
para manter a reentrância.
Implementa as funcionalidades do
módulo.
rectangle_new e rectangle_delete
fazendo malloc para esconder dados
do rectangle_t do cliente;
9 ago 2014 19 / 44Modularização de Código C
21. Globalcode – Open4education
Usando o rectangle
rectangle_t * rect;
rect = rectangle_new(NULL); /* Alloc a new rectangle */
if (!rect) {
perror(“Unable to alocate a rectangle”);
}
/* Set rectangle positon to (10,10) */
rectangle_set_position(rect, 10, 10);
rectangle_delete(rect); /* delete the rectangle */
rect = NULL;
Exemplo de uso do módulo rectangle:
9 ago 2014 21 / 44Modularização de Código C
23. Globalcode – Open4education
Usando o rectangle
rectangle_t * rec = rectangle_new(NULL);
rec->has_border;
Acessando os membros internos de rectangle diretamente não compila:
rectangle_t rect;
Porém usar rectangle_t na stack também não compila:
error: forward declaration of ‘rectangle_t {aka struct s_rectangle_instance}’
error: aggregate ‘rectangle_t rect’ has incomplete type and cannot be defined
Como rectangle_t é uma forward declaration, os membros internos da struct do módulo são
invisíveis ao código do cliente.
9 ago 2014 23 / 44Modularização de Código C
25. Globalcode – Open4education
Tipos de módulos
Quanto a instância:
de instância única;
ou multiplamente instanciável.
Quanto ao local da instância:
no heap somente;
ou no heap ou stack.
Quanto à implementação variável:
de implementação fixa;
ou selecionável na compilação;
rectangle
vector
framebuffer
9 ago 2014 25 / 44Modularização de Código C
27. Globalcode – Open4education
Instância na stack
A struct privada do módulo precisa ser visível ao
código do cliente.
Pode ter seus membros internos acessados
sem erro no compilador.
Informe ao cliente que aqueles dados são
privados:
Usando um header com nome “_private.h”
Nomeando a struct com “_private”
Acrescentando comentário na struct
E até colocando “__” nos nomes dos
membros da struct privada.
9 ago 2014 27 / 44Modularização de Código C
28. Globalcode – Open4education
Interface do vector
Arquivo vector.h – projeto chelper – commit 4273eed
http://github.com/felipe-lavratti/chelper/blob/4273eedd76d8fb6dc22dd978abad5ab7636bf39b/include/chelper/vector.h
#ifndef VECTOR_H_
#define VECTOR_H_
#include "private_helper_types.h“
...
typedef struct s_vector_private vector_t;
void vector_init(vector_t *, size_t item_size);
void vector_deinit(vector_t *);
size_t vector_size(vector_t *);
BUFFER_PTR vector_at(vector_t *, size_t pos);
void vector_add(vector_t *, BUFFER_PTR_RDOLY item);
void vector_remove(vector_t *, size_t pos);
void vector_clear(vector_t *);
...
#endif /* VECTOR_H_ */
Indicação de dados
privados no nome
Não é forward declaration
Não fazem malloc/free
Usam vector_t alocado
pelo cliente
9 ago 2014 28 / 44Modularização de Código C
29. Globalcode – Open4education
Dados privados
Arquivo private_helper_types.h – projeto chelper – commit 4273eed
http://github.com/felipe-lavratti/chelper/blob/4273eedd76d8fb6dc22dd978abad5ab7636bf39b/include/chelper/private_helper_types.h
#ifndef PRIVATE_HELPER_TYPES_H_
#define PRIVATE_HELPER_TYPES_H_
/*
* All types defined in this header should never be directly derreferenced nor
* have its member accessed by external code. These are private data belonging to
* internal modules only.
*
* Each of these structs has a non forward typedef meant to be allocated by client
* code and used only through chelper's public functions.
*/
struct s_vector_private
{
/* This data is private, do not touch it */
size_t __item_size;
uint32_t __used_slots;
uint32_t __buffer_total_slots;
uint8_t * __buffer;
};
...
#endif /* PRIVATE_HELPER_TYPES_H_ */
9 ago 2014 29 / 44Modularização de Código C
31. Globalcode – Open4education
Módulo framebuffer
(instância única e
implementação selecionável)
9 ago 2014 31 / 44Modularização de Código C
32. Globalcode – Open4education
Instância única
Tipo não é alocável pelo cliente.
É interno e static.
Não é reentrante.
9 ago 2014 32 / 44Modularização de Código C
33. Globalcode – Open4education
Interface do framebuffer
Arquivo framebuffer.h – projeto marsh – commit 7b4986a
http://github.com/felipe-lavratti/marsh/blob/7b4986ad0a9968d6ce140f8f46b4ac8cb16d4013/marsh/src/framebuffer.h
#ifndef FRAMEBUFFER_H_
#define FRAMEBUFFER_H_
…
void framebuffer_init(void);
void framebuffer_deinit(void);
pixel_t *framebuffer_start(void);
pixel_t *framebuffer_at(pixel_t x, pixel_t y);
size_t framebuffer_width(void);
size_t framebuffer_height(void);
const area_t * framebuffer_area(void);
void framebuffer_inform_written_area(size_t x,
size_t y, size_t width, size_t height);
#endif /* FRAMEBUFFER_H_ */
Não tem parâmetro de
instância.
9 ago 2014 33 / 44Modularização de Código C
34. Globalcode – Open4education
Interface do framebuffer
Arquivo framebuffer.c – projeto marsh – commit 7b4986a
http://github.com/felipe-lavratti/marsh/blob/7b4986ad0a9968d6ce140f8f46b4ac8cb16d4013/marsh/platform_src/test_mocks/framebuffer.c
#include "framebuffer.h“
#include "area.h“
...
static pixel_t* pFb;
...
void framebuffer_init()
{
if (!_fb_not_initd())
free(pFb);
pFb = (pixel_t *)calloc(FRAMEBUFFER_HEIGHT * FRAMEBUFFER_WIDTH, sizeof(pixel_t));
}
void framebuffer_delete()
{
if (_fb_not_initd())
return;
if (pFb)
free(pFb);
pFb = NULL;
}
...
Dados privados, nesse caso é só um apontador.
Módulo independente de plataforma pode ser
usado em código dependente de plataforma.
9 ago 2014 34 / 44Modularização de Código C
35. Globalcode – Open4education
Implementação selecionável
A implementação é escolhida na compilação, pelo
Makefile.
Todas implementam a mesma interface
Apenas uma interface de acesso ao módulo
Diversas implementações são possíveis.
Comumente usado para organizar código multi-
plataforma sem #ifdefs.
9 ago 2014 35 / 44Modularização de Código C
36. Globalcode – Open4education
Organização de pastas para
código multi-plataforma
Código dependente de plataforma
Código independente de plataforma
9 ago 2014 36 / 44Modularização de Código C
37. Globalcode – Open4education
Organização de pastas
Arquivos de implementação
são escolhidos de acordo
com o target do Makefile
Nome ruim, não passa a
funcionalidade para o
cliente, thread não diz
respeito à abstração, e sim
à implementação.
Para o código cliente do framebuffer, não
pode haver diferença de uso se compilado
para test_mocks ou linux_simulator.
9 ago 2014 37 / 44Modularização de Código C
39. Globalcode – Open4education
Usando o vector
#include <chelper/vector.h>
vector_t * vect_heap = malloc(sizeof(*vect_heap));
vector_t vect_stack;
vector_init(vect_heap, ...);
vector_init(&vect_stack, ...);
vect_heap->__item_size = 3;
vect_heap.__item_size = 3;
...
vector_deinit(vect_heap);
free(vect_heap);
vect_heap = NULL;
vector_deinit(&vect_heap);
Exemplo de uso do módulo vector, alocando no heap e na stack:
Mesmo esses acessos sendo considerados
ilegais, esse código compila.
9 ago 2014 39 / 44Modularização de Código C
40. Globalcode – Open4education
Usando o framebuffer
#include “framebuffer.h”
framebuffer_init();
...
pixel_t * fb_point = framebuffer_at(100, 20);
...
framebuffer_deinit();
Exemplo de uso do módulo framebuffer:
9 ago 2014 40 / 44Modularização de Código C
42. Globalcode – Open4education
Test-Driven Development for Embedded C
James W. Grenning
Para um excelente conteúdo sobre unit-testing e arquitetura modularizada.
A leitura do capítulo 11 “SOLID, Flexible, and Testable Designs” é
altamente recomendada.
Design Patterns for Embedded Systems in C
Bruce Powel Douglass
Como excelente referência de patterns em C para o emprego prático da
modularidade.
Referências
9 ago 2014 42 / 44Modularização de Código C
43. Globalcode – Open4education
Projeto Marsh e Projeto Chelper
Ambos são exemplos das técnicas de modularidade apresentadas.
http://github.com/felipe-lavratti
Chelper é uma biblioteca de extensão da
linguagem C, com código frequentemente utilizado
(vector, linked list, signal/slot, etc.)
Marsh é uma interface gráfica auto contida,
independente de OS e escrito em C, para rodar
tanto em firmware baremetal quanto sobre
sistemas operacionais.
CONTRIBUIDORES SÃO BEM VINDOS!
Referências
9 ago 2014 43 / 44Modularização de Código C