Polymorphism in C

Asked

Viewed 871 times

1

It is possible to use Polymorphism in a structured language such as C?

Languages such as c++,Java,etc(Object-oriented languages), have structures capable of inheriting functionalities for code reuse (one of the reasons), but in C, it is possible to use these programming techniques?

  • 1

    Related, I don’t know if it’s dup Polymorphism in procedural language

  • 1

    I wrote extensively sonre a related subject before yesterday, here: http://answall.com/questions/180783/como-montar--lista-de-objetos-gen%C3%A9ricos-em-c/181019#181019

  • I think it’s possible, but I’m sure it’s not worth it

  • 1

    I know it’s already answered, but just to comment: I would have to test this with a "pure" C compiler (http://www.cpm.z80.de/small_c.html), and not with a C/C compiler++.

3 answers

5


A form of "polymorphism" widely used in C programs is using function pointers, as has already been commented. You will find this kind of implementation in the Linux kernel, Openssl, etc.

Look at an example. I have declared several different animals and each emits the appropriate sound to it. Just define the correct behavior of each at startup.

#include <stdio.h>

// ponteiro para função de emitir som
typedef void (*func_emitir_som)(void);

// tipo genérico
typedef struct {
  func_emitir_som soar;
} Animal;

void latir(void) {
  puts("au! au!");
}

void miar(void) {
  puts("miau! miau!");
}

void mugir(void) {
  puts("muuu! muuu!");
}

int main(void) {
  Animal vaca     = { &mugir };
  Animal cachorro = { &latir };
  Animal gato     = { &miar  };

  vaca.soar();     // a vaca muge
  cachorro.soar(); // o cachorro late
  gato.soar();     // o gato mia

  return 0;
}

2

Yes. You will be responsible for the implementation. There are libraries ready as http://ooc-coding.sourceforge.net. I recommend using C++, if possible.

It is possible to create a struct to represent the class, with function pointers to access the variables of this class. For each function, include the parameter this, with the same type of struct. To construct the class, create a function that returns by parameter the object and, in this function, initialize the respective functions already declared the variables of the struct. These functions can be reallocated at runtime (I think this possibility is very cool).

For inheritance, create a variable in struct with the type of the inherited class (or a array for multiple inheritance).

For encapsulation create a opaque pointer to the struct.

However, this practice is very laborious and exploitation may not be achieved, depending on the complexity of the code. I recommend using functions in separate modules, treating structs as if they were objects, without creating function pointers in the struct (this practice only increases the complexity of the code and hinders the solution of bugs), solving interfaces, where necessary.

0

Simple C inheritance can be simulated very easily. However, this depends on making type coertions (typecasts), which prevents you from using the type system to catch some bugs simple. Let’s take an example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define ANOS 31556952 /* segundos por ano */
#define MKOBJ(obj) do { obj = malloc(sizeof(obj[0])); if (!obj) exit(1); } while (0)
#define RMOBJ(obj) (free(obj), obj = NULL)

/* Estrutura base */
typedef struct pessoa_t {
    char * nome;
    time_t dt_nascimento;
    unsigned char sexo;
} pessoa_t;

/* Estrutura derivada */
typedef struct empregado_t {
    pessoa_t base; /* NÃO é um ponteiro */
    unsigned int matricula;
    time_t dt_admissao;
} empregado_t;

void
imprimir_pessoa(pessoa_t * p) {
    printf("%s é %s de %d anos de idade.\n",
           p->nome,
           p->sexo == 'F' ? "uma moça" : "um rapaz",
           (time(NULL) - p->dt_nascimento) / ANOS
    );
}

void
imprimir_empregado(empregado_t * e) {
    imprimir_pessoa((pessoa_t *) e);
    printf("%s entrou na empresa há %d anos.\n",
           ((pessoa_t *) e)->sexo == 'F' ? "Ela" : "Ele",
           (time(NULL) - e->dt_admissao) / ANOS
    );
}

static void
criar_pessoa_aux(pessoa_t * self,
                 const char * nome,
                 char sexo,
                 int dia_nasc,
                 int mes_nasc,
                 int ano_nasc) {
    static struct tm tm;

    self->nome = strdup(nome);
    self->sexo = sexo;
    tm.tm_mday = dia_nasc;
    tm.tm_mon = mes_nasc - 1;
    tm.tm_year = ano_nasc - 1900;
    self->dt_nascimento = mktime(&tm);
}

pessoa_t *
criar_pessoa(const char * nome,
             char sexo,
             int dia_nasc,
             int mes_nasc,
             int ano_nasc) {
    pessoa_t * result;

    MKOBJ(result);
    criar_pessoa_aux(result, nome, sexo, dia_nasc, mes_nasc, ano_nasc);

    return result;
}

void
destruir_pessoa(pessoa_t * p) {
    RMOBJ(p->nome);
    RMOBJ(p);
}

empregado_t *
criar_empregado(const char * nome,
                char sexo,
                int dia_nasc,
                int mes_nasc,
                int ano_nasc,
                int matricula,
                int dia_adm,
                int mes_adm,
                int ano_adm) {
    empregado_t * result;
    static struct tm tm;

    MKOBJ(result);
    criar_pessoa_aux((pessoa_t *) result, nome, sexo, dia_nasc, mes_nasc, ano_nasc);
    result->matricula = matricula;
    tm.tm_mday = dia_adm;
    tm.tm_mon = mes_adm - 1;
    tm.tm_year = ano_adm - 1900;
    result->dt_admissao = mktime(&tm);

    return result;
}

void
destruir_empregado(empregado_t * e) {
    pessoa_t * p = (pessoa_t *) e;
    RMOBJ(p->nome);
    RMOBJ(e);
}

int
main(int argc, char ** argv) {
    pessoa_t * Alice;
    empregado_t * Beto, * Carol;

    /*                      -------nome------- sexo -nascimento-- -matr- ---admissão-- */
    Alice = criar_pessoa   ("Alice da Silva"  , 'F',  6, 12, 1999);
    Beto  = criar_empregado("Beto da Costa"   , 'M',  1,  5, 1974, 11111,  1,  1, 2007);
    Carol = criar_empregado("Carol dos Santos", 'F',  9,  3, 1990, 22222,  1, 10, 2015);

    imprimir_pessoa(Alice);
    imprimir_pessoa((pessoa_t *) Beto);
    imprimir_pessoa((pessoa_t *) Carol);
    puts("----------\n");
    // imprimir_empregado((empregado_t *) Alice); /* SEGFAULT! */
    imprimir_empregado(Beto);
    imprimir_empregado(Carol);

    return 0;
}

As you can see, it takes a lot of work: constructors have to automatically allocate variables and treat allocation errors, and then initialize them; destructors have to delete the strings allocated and then erasing themselves, etc. Of course, this will be the case for any architecture that is not native to the language.

That said, you can see how the empregado_t *, passed to a function expecting a pessoa_t *, does the right thing and works perfectly well since the first element of empregado_t is a structure pessoa_t. When you do the cast, the employee remains a valid person.

On the other hand, since you are doing type coercion, the compiler does not prevent you from forcing a function that receives a empregado_t * to receive a pessoa_t * other than a empregado_t *. In this case, you will only see the error running (with luck; the coercion can simply access data from another object as if it were the missing fields, and it will silently continue running with inconsistent data).

Like everything else in C, it’s a valid technique, but the programmer has to take responsibility for the Casts what makes.

  • The question talks about polymorphism and not inheritance.

  • @bigown: My understanding is that heritage is a mechanism for implementing polymorphism. If it is not, how do they differ?

  • They’re different mechanisms. In fact, inheritance allows you to apply polymorphism if you subtype easily, that’s all. Your code doesn’t do polymorphism, nor did I assess whether it actually makes inheritance, it seems to me that it wasn’t even done. Some things that may help: http://answall.com/q/152266/101, http://answall.com/a/153042/101, http://answall.com/q/154421/101, http://answall.com/q/53108/101, http://answall.com/q/89309/101, http://answall.com/q/158579/101, http://answall.com/q/151243/101 this shows the behaviour substitution without polymorphism.

  • Everything that was done there is manual. What you’re thinking is inheritance is actually just composition. Obviously C doesn’t allow everything to be done transparently, but at least need to.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.