INF1018 - Software Básico

Co-rotinas como iteradores

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.


  1. No exercício deste laboratório, vamos utilizar uma estrutura de dados que oferece uma coleção de inteiros. Pegue agora os arquivos:

    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:

    • void *criaIterador (Colecao *l);
    • int ativaIterador (void *iterador);
    • void destroiIterador (void *iterador);
    (que ainda não estão totalmente implementadas em 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!

  2. Vamos agora completar a implementação da nossa coleção, definida no arquivo 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.

  3. Agora, com a implementação completa, compile e teste o seu programa!

    Para compilá-lo, você vai usar:

    gcc -Wall -m32 -Wa,--execstack -o testaColecao core.s corotinas.c colecao.c testaColecao.c