FACIN logo

Modularização de Programas em C

Marcelo Cohen, FACIN - PUCRS

Motivação: Por que modularizar ?

Solução: Modularização

Agrupam-se as funções relacionadas em módulos. Para cada módulo são utilizados dois arquivos:

Para usar uma função em outro módulo, basta então incluir o arquivo .h correspondente

Criação do header> da biblioteca de funções (2)

Para exemplificar, criaremos uma biblioteca simples de funções matemáticas. Primeiro, escrevemos os protótipos das funções no arquivo bibfunc.h:

float fatorial(int v);
float somatorio(int v);
...

Criação do header> da biblioteca de funções (2)

Porém, se por engano esse arquivo for incluído mais de uma vez, haverá uma duplicidade de todas as definições. Como isso pode acontecer facilmente, acrescentamos 3 linhas no arquivo .h:

#ifndef BIBFUNC_H
#define BIBFUNC_H

float fatorial(int v);
float somatorio(int v);
...
#endif

Implementação das funções

A seguir, escrevemos o arquivo bibfunc.cpp, que contém a implementação das funções definidas anteriormente.

#include "bibfunc.h"

float fatorial(int v) {
  int res = 1;
  for(int i=1; i<=v; i++)
    res = res * i;
  return res;
}
...

Programa Principal

Finalmente, o programa principal deve ser implementado em um arquivo separado, como por exemplo main.cpp:

#include <iostream>
#include "bibfunc.h"

using namespace std;

int main()
{
    cout << "Digite um valor: ";
    int v;
    cin >> v;
    cout << "Fatorial: " << fatorial(v);
    cout << "Somatório: " << somatorio(v);
    ...
}

Compilação dos Módulos

Para compilar o programa completo, é preciso indicar todos os fontes na linha de comando:

Porém, essa estratégia tem algumas implicacões:

Para um programa tão pequeno, isso não faz tanta diferença...

Mas se o programa for composto por muitos módulos, compilá-los separadamente pode ter um custo considerável, além de não ser muito prático.

Compilação Modular: Makefiles

Um Makefile é um roteiro de compilação:

O nome do arquivo deve ser preferencialmente Makefile.
Para utilizá-lo, basta digitar o comando make no terminal.

all: main

main: main.o bibfunc.o
	g++ -o main main.o bibfunc.o

main.o: main.cpp
	g++ -c main.cpp

bibfunc.o: bibfunc.cpp
	g++ -c bibfunc.cpp

Compilação Modular: Makefiles (2)

Os comandos de compilação não parecem meio repetitivos ? Podemos melhorar o Makefile, criando regras genéricas:

CXXFLAGS = -Wall -g    # Opções do compilador: todos warnings e debug info

PROG = main
FONTES = main.cpp bibfunc.cpp
OBJETOS = $(FONTES:.cpp=.o)

$(PROG): $(OBJETOS)
	g++ $(CXXFLAGS) $(OBJETOS) -o $@

clean:
	-@ rm -f $(OBJETOS) $(PROG)

Compilação Modular: Makefiles (3)

Mas e como especificar dependências automáticas para os .h ?
Uma forma é incluir uma regra que use o comando makedepend:

...
clean:
	-@ rm -f $(OBJETOS)

depend:
	makedepend -- ${CFLAGS} -- ${FONTES}

Ao digitarmos make depend, o comando makedepend será ativado, e verificará as dependências de cada módulo, incluindo estas no final do próprio Makefile:

...
depend:
	makedepend -- ${CFLAGS} -- ${FONTES}

# DO NOT DELETE

main.o: bibfunc.h
bibfunc.o: bibfunc.h

Compilação Modular: Alternativas ao make

Já existem alguns sistemas alternativos ao make: