INF1018- Software Básico - 2012.2

Segundo Trabalho

Gerador de Funções

O objetivo deste trabalho é implementar em C uma função gera_func que recebe como parâmetros uma função f e a descrição de seus parâmetros e retorna uma "nova versão" de f, gerada dinamicamente que, ao ser chamada, só precisa receber os valores dos parâmetros que não foram "amarrados" (fixados) por gera_func.

Como exemplo, considere a função pow, da biblioteca matemática, que retorna o valor de seu primeiro parâmetro (x) elevado ao segundo (y):

   double pow (double x, double y);
A função gera_func nos permite criar dinamicamente uma nova função que sempre eleva o seu parâmetro ao quadrado. Para criar essa função, gera_func amarra o segundo parâmetro de pow, construindo, em tempo de execução, o código de uma função que chama pow com dois parâmetros: o primeiro é seu próprio parâmetro e o segundo é o valor 2.0. Quando pow retornar, essa função retorna o valor retornado por pow.

Também deve ser implementada uma função libera_func, que é responsável por liberar a função criada dinamicamente por gera_func.

Leia com atenção o enunciado do trabalho e as instruções de entrega.

Em caso de dúvidas, não invente. Pergunte!


Especificação das funções:

A função gera_func deve ter o seguinte protótipo:

   void* gera_func (void* f, int n, Parametro params[]);
onde f tem o endereço da função a ser chamada, o array params contém a lista de parâmetros de f e n é o número de parâmetros descritos por params.

O tipo Parametro é definido como:

   typedef enum {INT_PAR, CHAR_PAR, DOUBLE_PAR, PTR_PAR} TipoParam;

   typedef struct {
      TipoParam tipo;  /* indica o tipo do parâmetro */
      int amarrado;    /* indica se o parâmetro deve ter um valor amarrado */
      union {
         int v_int;
         char v_char;
         double v_double;
         void* v_ptr;
      } valor;         /* define o valor do parâmetro se este for amarrado */
   } Parametro;

A função libera_func deve ter o protótipo a seguir:

   void libera_func (void* func);

O arquivo gera_func.h contém as definições acima, e pode ser pego AQUI. O trabalho deve seguir estritamente as definições constantes nesse arquivo.


Exemplos de uso:

O programa abaixo usa gera_func para criar dinamicamente uma função que eleva números ao quadrado e depois invoca essa função para obter os quadrados de 1 a 10:
   #include <math.h>
   #include <stdio.h>
   #include "gera_func.h"

   typedef double (*func_ptr) (double);

   int main (void) {
      Parametro params[2];
      func_ptr f_quadrado = NULL;
      double d;
      int i;

      params[0].tipo = DOUBLE_PAR;
      params[0].amarrado = 0;
      params[1].tipo = DOUBLE_PAR;
      params[1].amarrado = 1;
      params[1].valor.v_double = 2.0;

      f_quadrado = (func_ptr) gera_func (pow, 2, params);

      for (i = 1; i <= 10; i++) {
         d = f_quadrado(i);
         printf("%d ^ 2 = %f\n", i, d);
      }

      libera_func(f_quadrado);
      return 0;
   }
Um outro exemplo é uma implementação genérica de BubbleSort que recebe como argumentos o array a ser ordenado e uma função para a comparação de dois elementos. O arquivo gensort.c mostra um exemplo dessa implementação genérica e de funções que poderiam ser usadas como seu segundo argumento. A função gera_func poderia ser usada para gerar duas versões dessa implementação, para ordenações em ordem crescente e em ordem decrescente. Essas duas versões podem ser criadas "amarrando-se" a função de comparação.


Implementação:

A função gera_func deve ser implementada em C, mas ela deve gerar, em um bloco de memória alocado, um trecho de código em linguagem de máquina que implementa a função criada dinamicamente. O valor de retorno de gera_func será um ponteiro para esse bloco de memória.

De forma geral, gera_func irá percorrer o array com a descrição dos parâmetros e gerar um código em linguagem de máquina que:

Recomendamos fortemente uma implementação gradual, desenvolvendo e testando passo-a-passo cada nova funcionalidade implementada. Comece, por exemplo, com um esqueleto que aloca espaço para o código a ser gerado, coloca um código bem conhecido nessa região e retorna o endereço da região alocada. Teste a chamada a essa função gerada dinamicamente. Para obter um código "bem conhecido" você pode compilar um arquivo assembly contendo uma função simples (o retorno do seu parâmetro, por exemplo) usando:
    minhamaquina> gcc -m32 -c code.s
A opção -c é usada para compilar e não gerar o executável. Depois de compilar, veja o código de máquina gerado usando:
    minhamaquina> objdump -d code.o
A seguir, por exemplo, implemente e teste a geração de código para chamar uma função que retorna o valor de seu parâmetro inteiro, sem fixar e depois fixando o valor desse parâmetro. Vá depois aumentando o número e os tipos de parâmetros tratados. Use os exemplos dados no enunciado do trabalho mas faça também testes mais completos!


Prazo:

O trabalho deve ser entregue até meia-noite do dia 26 de novembro por mensagem eletrônica.

Será descontado um ponto por cada dia de atraso (incluindo sábado, domingo e feriados).


Entrega:

Leia com atenção as intruções para entrega a seguir:


Observações: