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 um conjunto de parâmetros e retorna uma "nova versão" de
f
, flinha
, gerada dinamicamente que, ao ser chamada,
chama a função original da maneira definida em gera_func
.
Os argumentos passados a gera_func
podem fazer com que
flinha
já tenha valores constantes "amarrados" a um ou mais dos
argumentos de f
, e também definem a posição em que os argumentos
a serem usados na chamada a f
aparecerão na chamada a flinha
(os parâmetros são numerados da esquerda para a direita na chamada da função).
Para pensar em um 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!
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 */ int posicao; /* indica em que posição esse parâmetro vai aparecer na chamada da função gerada */ 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.
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 x); 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[0].posicao = 1; 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; }
gera_func
para criar uma nova função,
foo
, que chama a função boo
, definida no mesmo
arquivo,
invertendo os argumentos passados:
#include <stdio.h> #include <string.h> #include "gera_func.h" int boo (int a, int b) { return a-b; } typedef int (*func_ptr) (int x, int y); int main (void) { Parametro params[2]; func_ptr foo = NULL; int a, b; params[0].tipo = INT_PAR; params[0].amarrado = 0; params[0].posicao = 2; params[1].tipo = INT_PAR; params[1].amarrado = 0; params[1].posicao = 1; foo = gera_func (boo, 2, params); a = 2; b = 3; printf ("boo(%d,%d) = %d\n", a, b, boo(a,b)); printf ("foo(%d,%d) = %d\n", a, b, foo(a,b)); return 0; }
memcmp
,
da biblioteca padrão de C, para determinar se diferentes strings são prefixos
de uma determinada string fixa.
A função memcmp
recebe como argumento duas strings e um número n
e verifica se os n primeiros bytes das duas são iguais:
int memcmp(const void *s1, const void *s2, size_t n);Podemos usar a função
gera_func
para fixar uma das strings,
passando para a nova função apenas a string que queremos testar e o tamanho
do prefixo que desejamos testar:
#include <stdio.h> #include <string.h> #include "gera_func.h" typedef int (*func_ptr) (void* candidata, size_t n); char a[] = "quero saber se a outra string é um prefixo dessa aqu"; int main (void) { Parametro params[3]; func_ptr e_prefixo = NULL; char um[] = "quero saber se por acaso falei alguma bobagem"; int tam; params[0].tipo = PTR_PAR; params[0].amarrado = 0; params[0].posicao = 1; params[1].tipo = PTR_PAR; params[1].amarrado = 1; params[1].valor.v_ptr = a; params[2].tipo = INT_PAR; params[2].amarrado = 0; params[2].posicao = 2; e_prefixo = (func_ptr) gera_func (memcmp, 3, params); tam = 3; printf ("'%s' e' prefixo-%d de '%s'? %s\n", um, tam, a,e_prefixo (um, tam)?"NAO":"SIM"); tam = 15; printf ("%s e' prefixo-%d de '%s'? %s\n", um, tam, a,e_prefixo (um, tam)?"NAO":"SIM"); tam = 25; printf ("'%s' e' prefixo-%d de '%s'? %s\n", um, tam, a,e_prefixo (um, tam)?"NAO":"SIM"); return 0; }
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:
push
para empilhar os parâmetros
para a função original, respeitando os tipos e eventuais valores amarrados
especificados no array params
;
call
;
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 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!
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:
gera_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
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).