cria_func
que recebe como parâmetros uma função f
e a descrição de seus parâmetros
e retorna um ponteiro para uma nova função, gerada dinamicamente,
que, ao ser chamada, só precisa do valor dos argumentos que não foram
"fixados" por cria_func
.
Como exemplo, imagine que temos uma função:
int mult (int a, int b) { return a*b; }
A função cria_func
nos permitiria criar uma "nova versão" de mult
que sempre multiplicasse seu único parâmetro por um número fixo
(por exemplo, 2).
Dizemos que cria_func
amarra alguns valores de parâmetros
de f
.
Para isso, cria_func
deve receber como parâmetros
uma função f
qualquer (um ponteiro de função em C)
o número e uma descrição da lista dos parâmetros de f
,
contendo os tipos dos parâmetros, e, no caso em que se deseja amarrar
valores, os valores.
A função cria_func
deve então gerar uma nova função
que, quando chamada, vai chamar f
passando como parâmetros
os valores que tiverem sido amarrados na sua criação.
Os parâmetros de f
que não tiveram valores amarrados
permanecem como parâmetros da nova função
(gerada por cria_func
).
A função cria_func
deve ter o seguinte protótipo:
void* cria_func (void* f, int n, Parametro params[]);onde o array
params
contém a lista de parâmetros
de f
e n
é o número de parâmetros.
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 e_constante; /* indica se o parâmetro deve ter um valor constante */ union { int v_int; char v_char; double v_double; void* v_ptr; } valor; /* define o valor do parâmetro se este for constante */ } Parametro;Também deve ser implementada a função
void libera_func (void* func);que é responsável por liberar uma função criada dinamicamente.
Considere que f
é a função
double pow (double x, double y);
o trecho de programa a seguir cria dinamicamente uma função que eleva
números ao quadrado:
#include <math.h> #include <stdlib.h> #include "cria_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].e_constante = 0; params[1].tipo = DOUBLE_PAR; params[1].e_constante = 1; params[1].valor.v_double = 2.0; f_quadrado = (func_ptr) cria_func (pow, 2, params); for (i = 1; i <= 10; i++) { d = f_quadrado(i); printf("%d ^ 2 = %f\n", i, d); } return 0; }Nesse exemplo,
cria_func
cria em tempo de execução uma nova
função baseada na função pow
, que amarra o parâmetro
y
com o valor 2.0 e deixa o parâmetro x
livre
para ser definido posteriormente.
Desta forma, essa nova função deve receber um parâmetro double
(correspondente ao parâmetro x
de pow
)
e retorna um outro valor double
como resultado.
Como o parâmetro y
ficou amarrado ao valor 2.0,
f_quadrado
sempre retorna o quadrado do valor de seu argumento.
Um outro exemplo é uma implementação genérica de BubbleSort que recebe como argumento, além
do array a ser ordenado, uma função para a comparação de dois elementos. O arquivo
gensort.c mostra um exemplo de implementação genérica,
e de funções que poderiam ser usadas como argumento.
A função cria_func
poderia ser usada para gerar versões
dessa implementação genérica para ordenação em ordem crescente ou decrescente, sendo essa
ordem definida fixando-se a função de comparação.
A função cria_func
deve alocar um bloco de memória para escrever o
código em linguagem de máquina que implementa a função criada dinamicamente.
O valor de retorno de cria_func
será um ponteiro para essa área.
De forma geral, cria_func
irá percorrer o array com a descrição
dos parâmetros e gerar um código em linguagem de máquina que:
push
para empilhar os parâmetros
para a função original, respeitando os tipos e eventuais valores constantes
especificados no array params
;
call
;
Recomendamos fortemente que você faça 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 C (ou assembly) contendo uma função simples (o retorno do seu parâmetro, por exemplo) usando:
minhamaquina> gcc -O0 -c code.cou
minhamaquina> gcc -m32 -c code.sA 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.oA seguir, por exemplo, implemente e e teste a geração de código para chamada a uma função que retorna o valor de seu parâmetro inteiro, sem fixar e 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!
Será descontado
um ponto
por cada dia de atraso (incluindo sábado, domingo e feriados).
Leia com atenção as intruções para entrega a seguir:
cria_func
e libera_func
. Esse arquivo não deve conter a
função main
.
/* Nome_do_Aluno1 Matricula Turma */ /* Nome_do_Aluno2 Matricula Turma */Coloque também no relatorio o nome dos integrantes do grupo
cria_func.h
pode ser pego
AQUI.
O trabalho deve seguir estritamente as definições constantes nesse arquivo.
objdump
será provavelmente a maior fonte de referência.
Os opcodes das instruções em linguagem de máquina que devem ser
geradas dinamicamente também podem ser obtidos
no Intel Pentium Instruction Set Reference Manual (Volume 2).