ponteiros void e funções genéricas [RESOLVIDO]

1. ponteiros void e funções genéricas [RESOLVIDO]

ian cléver sales fernandes
ianclever

(usa Arch Linux)

Enviado em 02/10/2014 - 12:18h

Boa tarde,

Programando já a algum tempo em c, o tamanho dos códigos tem aumentado progressivamente, e algumas dificuldades foram encontradas, devido a isso resolvi criar algumas bibliotecas próprias para melhorar algumas coisas que o c não tem por padrão, o grande x da questão é que eu quero criar cada função uma única vez de modo que ela funcione para qualquer tipo de dados. Em busca disso procurei sobre ponteiros void e descobri que os mesmos podem apontar para qualquer tipo de dados, até aí tudo bem mas a partir do momento que uso ele como parâmetro de uma função como eu vou identificar dentro do escopo da função , qual o tipo de dados para o qual ele está apontando?

ex.:


#include <stdio.h>

void *funcao(void *p){
/*aqui dentro dessa função, como eu vou saber para qual tipo de dados o ponteiro p está apontando? (sem usar um parâmetro auxiliar para me informar isso, já que é óbvio que isso não se aplicaria as structs, as variáveis com typedef)*/
}
int main(void){
int a, *p;
char b, *p2;
*p = (int *)funcao(&a);
*p2 = (char *)funcao(&b);
}



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 02/10/2014 - 14:11h

Sem um parâmetro auxiliar, em C, não dá para fazer. Pegue exemplos de funções como printf() e scanf(). O string de formatação é a única forma pela qual a função tem como saber como lidar com os demais argumentos.

Note, porém, que todas as possibilidades têm de estar previstas de antemão. Se você quiser estender sua função para outros tipos de dados além dos simples tipos nativos, teria de colocar dentro dessa função o código específico para cada tipo de dados derivado que você viesse a criar. No fim das contas, sua tentativa de simplificação se tornaria um pesadelo, dando mais trabalho de implementação e manutenção do que daria criar uma função separada para cada tipo.

Caminhos que você pode seguir dependem do que você quer ou precisa fazer. Se você trabalhar basicamente com expressões matemáticas relativamente simples e tipos nativos, macros do preprocessador podem ser suficientes. Por exemplo:

/*
O tipo devolvido por ABS() é, por construção, o mesmo tipo do argumento
com que for chamada.
*/
#define ABS(x) ((x)<0? -(x): (x))

/*
SIGNAL(x) pode ser chamada com qualquer tipo nativo, mas vemos que,
também por construção, qualquer valor devolvido será do tipo “int”.
*/
#define SIGNAL(x) ((x)<0? -1: (x)>0? 1: 0)


Macros porém têm seus problemas. Um deles é que é possível indefinir e redefinir seu significado, sem que o compilador emita qualquer alerta. Outro são os famigerados efeitos colaterais (por exemplo: se a==1 e eu chamo ABS(--a), o valor devolvido será -1, que é um absurdo, mas decorrência necessária e incontornável do fato de que o argumento entre parênteses é substituído exatamente do modo como foi escrito no momento da invocação da macro).

--

Eu gosto de C++ e advogo seu uso, não apenas por causa do caso em questão. Mas, no caso em questão, o C++ tem uma notação pronta para definição de funções e tipos de dados genéricos, por meio de templates (ou gabaritos). Junto com isso, ele permite estender os operadores nativos da linguagem (como +, *, == etc.) para tipo de dados definidos pelo usuário, e isso facilita ainda mais a aplicação de templates. Veja o seguinte exemplo.

// my_abs() retorna um dado do mesmo tipo do argumento
template <typename T> inline T my_abs(const T &x){
// Antes de comparar com 0, transforma o 0 num valor compatível com o tipo T.
return x<T(0)? -x: x;
}

// my_signal() retorna sempre int, embora aceite diferentes tipos de argumento
template <typename T> inline int my_signal(const T &x){
// Antes de comparar com 0, transforma o 0 num valor compatível com o tipo T.
const T z(0);
return x<z? -1: x>z? 1: 0;
}


// Tipo de dados que representa um número racional (fração com numerador e
// denomindor inteiros) -- só para dar exemplo de uso dos templates acima.
class rational_number {
private:
int numerator, denominator;

public:

// Constrói um número racional a partir de um inteiro.
rational_number(int x){
numerator=x;
denominator=1;
}

// Constrói um número racional a partir de um numerador e um denominador.
rational_number(int n, int d){
if(d<0){
numerator=-n; denominator=-d;
}
else{
numerator=n; denominator=d;
}
}

inline bool operator <(const rational_number &other){
return numerator*other.denominator < other.numerator*denominator;
}

inline bool operator >(const rational_number &other){
// Aproveito o fato de que (a>b)=(b<a) e reutilizo a função acima.
// Como são funções inline, isso não gera overhead no momento da
// execução do programa.
return other < *this;
}

inline bool operator !=(const rational_number &other){
return (*this<other) || (*this>other);
}

inline bool operator ==(const rational_number &other){
return !(*this!=other);
}

inline rational_number operator -(){
return rational_number(-numerator, denominator);
}
};


/*
Com as definições do construtor de conversão de tipo e dos operadores
de comparação, você pode chamar my_abs(valor_rational_number) e
my_signal(valor_rational_number), do mesmo modo como pode chamar
my_abs(valor_int), my_abs(valor_double), my_abs(valor_char),
my_abs(valor_float) etc.
*/


Só que não existe mágica. Apesar de o C++ facilitar a notação, no momento em que você aplica uma operação genérica a um determinado tipo de dado, o compilador gera código específico para aquele tipo de dado, como se você mesmo tivesse escrito aquele código literalmente. Você não vê isso no programa fonte que escreve e lê, mas a versão de my_abs() aplicada a um valor do tipo rational_number é diferente da versão de my_abs() que recebe argumento inteiro, e ambas são diferentes da que recebe argumento double, e assim por diante. No entanto, todas as diferentes versões que porventura forem usadas ao longo do programa estarão presentes no programa executável gerado, sendo absolutamente distintas entre si.





Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts