Neste laboratório, vamos explorar o uso de co-rotinas como iteradores de estruturas de dados. Novamente, para começar, pegue os arquivos:
e coloque em um diretório novo chamado labcoro.Examine esses arquivos. Observe que Colecao é um tipo abstrato, cuja representação
só é vista dentro de colecao.c
(essa representação utiliza uma lista encadeada...)
O programa usuário de uma coleção só pode acessá-la através das operações oferecidas
em sua interface, descrita em colecao.h
.
Considere o seguinte problema.
Imagine que você quer
calcular a soma e o produto dos elementos numa coleção
já existente.
Você poderia fazer isso em testaColecao.c
...?
Em princípio, isso não é possível, devido à estrutura de encapsulamento da coleção (lembre-se que o usuário de uma coleção não tem acesso à sua representação interna). Repare, contudo, que o tipo abstrato Colecao oferece um iterador com o qual será possível realizar tarefas como a descrita acima.
Veja que há três funções na interface colecao.h
:
colecao.c
).
A função criaIterador
cria, e retorna, um novo iterador para a
coleção passada como parâmetro.
Repare que o tipo desse iterador é void *
.
O usuário da coleção não conhece o tipo real do iterador (na verdade,
uma co-rotina, como veremos adiante).
A função ativaIterador
recebe um iterador como parâmetro
e retorna o próximo valor inteiro armazenado na coleção.
Caso toda a coleção já tenha sido toda percorrida (ou seja, não há mais elementos
para o iterador enviar) essa função retorna o valor MININT
(definido no arquivo de cabeçalho values.h
).
Quando o iterador retornar esse valor, ele não poderá ser ativado novamente.
Finalmente, destroiIterador
destrói o iterador
passado como argumento.
Complete agora testaColecao.c
, obtendo os
valores desejados (a soma e o produto dos elementos das coleções
criadas pela main
).
Para isso, você deverá criar um iterador para cada uma dessas coleções,
e usá-lo para obter seus elementos.
Não esqueça de destruir os iteradores quando terminar de usá-los.
Note que o uso do iterador não requer que seu usuário conheça sua implementação!
colecao.c
.
Para isso, vamos completar as funções relacionadas ao
iterador da coleção, que será implementado como uma co-rotina.
A função criaIterador
deve criar e retornar uma nova co-rotina,
cujo função principal (o seu "corpo") é iteraColecao
.
A função iteraColecao
deve percorrer a lista que representa a coleção,
fornecendo com yields
cada um dos seus elementos.
Um "esqueleto" dessa função já está no arquivo colecao.c
.
Note que ela é declarada como static
no arquivo
colecao.c
pois não deve ser usada diretamente
de fora desse arquivo!
Como a coleção a ser iterada é argumento apenas para a função
criaIterador
, precisamos "amarrá-la" à co-rotina.
Para isso, após criar a co-rotina, criaIterador
já deve fazer um primeiro resume
para poder passar
a coleção como parâmetro para a função iteraColecao
.
Por sua vez, após guardar o ponteiro para a coleção a ser iterada,
iteraColecao
faz um yield
para
retornar o controle a criaIterador
.
Como o valor passado nesse yield
não será usado
por criaIterador
,
a função passa o valor NO_PARAM
(definido em corotinas.h
).
Repare que o valor MININT é retornado no final de iteraColecao
.
Como vimos antes,
esse valor corresponde ao final da coleção,
e será o resultado do último resume
para a co-rotina.
A função ativaIterador
recebe como parâmetro um iterador
criado por criaIterador
, ou seja, uma co-rotina.
Ela deve, portanto, reativar a co-rotina e retornar o valor fornecido por ela.
Como não há valor a ser passado para a co-rotina, o segundo parâmetro
de coro_resume
pode ser NO_PARAM
.
Finalmente, destroiIterador
deve liberar a co-rotina que
implementa o iterador.
Para compilá-lo, você vai usar:
gcc -Wall -m32 -Wa,--execstack -o testaColecao core.s corotinas.c colecao.c testaColecao.c