52

Is it possible to model inheritance using C? How? Sample code will help.

Edit: I am looking to inherit both data and methods. Containership alone will not help. Substitutability - using any derived class object where a base class object works - is what I need.

Agnel Kurian
  • 53,593
  • 39
  • 135
  • 210
  • What is the reason for doing that? – Artem Barger Aug 06 '09 at 06:33
  • I have some C code using mostly containership. The project has come to a point where substitutability (an "is a" relationship) has become very desirable. – Agnel Kurian Aug 06 '09 at 06:47
  • basically answers all your questions: [http://stackoverflow.com/questions/415452/object-orientation-in-c/415536#415536](http://stackoverflow.com/questions/415452/object-orientation-in-c/415536#415536) – Junier Aug 06 '09 at 06:48
  • With C, you can write (almost) object-oriented code, but it wouldn't be strongly-typed. – Zaxter Sep 03 '15 at 11:46

8 Answers8

36

It is very simple to go like this:

struct parent {
    int  foo;
    char *bar;
};

struct child {
    struct parent base;
    int bar;
};

struct child derived;

derived.bar = 1;
derived.base.foo = 2;

But if you use MS extension (in GCC use -fms-extensions flag) you can use anonymous nested structs and it will look much better:

struct child {
    struct parent;    // anonymous nested struct
    int bar;
};

struct child derived;

derived.bar = 1;
derived.foo = 2;     // now it is flat
qrdl
  • 31,423
  • 13
  • 52
  • 82
  • nice, but now CLang won't compile this.. – makapuf Feb 19 '18 at 21:04
  • @makapuf C11 supports it as a standard feature (https://en.wikipedia.org/wiki/C11_(C_standard_revision)#Changes_from_C99), so `clang` should support it with `-std=c11` – qrdl Feb 20 '18 at 06:52
  • well, not exactly it seems. C11 accepts it with anonymous untagged structs, so you could embed another unnamed struct but cannot reuse a previous struct definition... (why it's the case I don't know). – makapuf Feb 28 '18 at 22:44
  • @makapuf yes you can. I just tested it and you can use previously defined structs and previously typedef'd structs. But you have to use C11 AND the flag, check https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html – m4l490n Oct 30 '19 at 16:13
19

You can definitely write C in a (somewhat) object-oriented style.

Encapsulation can be done by keeping the definitions of your structures in the .c file rather than in the associated header. Then the outer world handles your objects by keeping pointers to them, and you provide functions accepting such pointers as the "methods" of your objects.

Polymorphism-like behavior can be obtained by using functions pointers, usually grouped within "operations structures", kind of like the "virtual method table" in your C++ objects (or whatever it's called). The ops structure can also include other things such as constants whose value is specific to a given "subclass". The "parent" structure can keep a reference to ops-specific data through a generic void* pointer. Of course the "subclass" could repeat the pattern for multiple levels of inheritance.

So, in the example below, struct printer is akin to an abstract class, which can be "derived" by filling out a pr_ops structure, and providing a constructor function wrapping pr_create(). Each subtype will have its own structure which will be "anchored" to the struct printer object through the data generic pointer. This is demontrated by the fileprinter subtype. One could imagine a GUI or socket-based printer, that would be manipulated regardless by the rest of the code as a struct printer * reference.

printer.h:

struct pr_ops {
    void (*printline)(void *data, const char *line);
    void (*cleanup)(void *data);
};

struct printer *pr_create(const char *name, const struct output_ops *ops, void *data);
void pr_printline(struct printer *pr, const char *line);
void pr_delete(struct printer *pr);

printer.c:

#include "printer.h"
...

struct printer {
    char *name;
    struct pr_ops *ops;
    void *data;
}

/* constructor */
struct printer *pr_create(const char *name, const struct output_ops *ops, void *data)
{
    struct printer *p = malloc(sizeof *p);
    p->name = strdup(name);
    p->ops = ops;
    p->data = data;
}

void pr_printline(struct printer *p, const char *line)
{
    char *l = malloc(strlen(line) + strlen(p->name) + 3;
    sprintf(l, "%s: %s", p->name, line);
    p->ops->printline(p->data, l);
}

void pr_delete(struct printer *p)
{
    p->ops->cleanup(p->data);
    free(p);
}

Finally, fileprinter.c:

struct fileprinter {
    FILE *f;
    int doflush;
};

static void filepr_printline(void *data, const char *line)
{
    struct fileprinter *fp = data;
    fprintf(fp->f, "%s\n", line);
    if(fp->doflush) fflush(fp->f);
}

struct printer *filepr_create(const char *name, FILE *f, int doflush)
{
    static const struct ops = {
        filepr_printline,
        free,
    };

    struct *fp = malloc(sizeof *fp);
    fp->f = f;
    fp->doflush = doflush;
    return pr_create(name, &ops, fp);
}
Jérémie Koenig
  • 1,118
  • 5
  • 11
  • 12
    +1. Also as historical note - the first C++ compilers did not output machine code but C code because it was faster and easier to implement (the C to machine code layer existing). So, it gdefinitely can be done in C. – TomTom Dec 19 '10 at 21:01
5

Yes, you can emulate heritance en C using the "type punning" technique. That is the declaration of the base class (struct) inside the derived class, and cast the derived as a base:

struct base_class {
  int x;
};

struct derived_class {
  struct base_class base;
  int y;
}

struct derived_class2 {
  struct base_class base;
  int z;
}
void test() {
  struct derived_class d;
  struct derived_class2 d2;

  d.base.x = 10;
  d.y = 20;

  printf("x=%i, y=%i\n", d.base.x, d.y);
}

But you must to declare the base class in the first position in you derived structure, if you want to cast the derived as base in a program:

 struct base *b1, *b2;

 b1 = (struct base *)d;
 b2 = (struct base *)d2;

 b1->x=10;
 b2->x=20;
 printf("b1 x=%i, b2 x=%i\n", b1->x, b2->x);

In this snippet you can use the base class only.

I use this technique in my projects: oop4c

curif
  • 71
  • 1
  • 4
2

It should be possible, at least to some extent.

What exactly do you need to model? The inheritance of the data or the methods?

Edit: Here's a short article that I found: http://fluff.info/blog/arch/00000162.htm

rslite
  • 73,388
  • 4
  • 40
  • 45
2

Since early versions of C++ were mainly a preprocessor that converted into C, it's deinitely possible.

mhawke
  • 75,264
  • 8
  • 92
  • 125
  • This should've been a comment, as it doesn't answer OP's questions. However, it is now already noted in a comment on another answer, so this answer should be deleted. – cp.engr Jan 29 '20 at 17:52
2

I've used an object system in C that used late-bound methods, which allowed for object-orientation with reflection.

You can read about it here.

Ryan Fox
  • 9,416
  • 5
  • 34
  • 48
  • indeed; the subject is much too broad to fully cover in a single answer on stackoverflow. On first sight, I think the article linked to covers a good deal. I like the explanation of Miro Samek in " Practical Statecharts in C/C++" (1st edition only) – Adriaan Aug 06 '09 at 07:02
2
#include <stdio.h> 

///////Class Cobj
typedef struct Cobj{
    int x;
    void (*setptr)(char * s,int val);
    int (*getptr)(char * s);
} Cobj;

void set(char * s,int val)
{
    Cobj * y=(Cobj *)s;
    y->x=val;
}
int get(char * s){
    Cobj * y=(Cobj *)s;
    return y->x;
}
///////Class Cobj
Cobj s={12,set,get};
Cobj x;

void main(void){
    x=s;
    x.setptr((char*)&x,5);
    s.setptr((char*)&s,8);
    printf("%d %d %d",x.getptr((char*)&x),s.getptr((char*)&s) ,sizeof(Cobj));
}
Exikle
  • 1,145
  • 1
  • 16
  • 40
Raks
  • 21
  • 2
1

This link might be useful -> link

Basic example will be like follow

 struct BaseStruct
{
  // some variable
}


struct DerivedStruct
{
  struct BaseStruct lw;
  // some more variable
};
malay
  • 1,398
  • 4
  • 16
  • 28