-2

I am a young student who learned C two years ago. I just discovered more complex things like object oriented C.

My first question is:

1) How do you access your variables? The goal would be to have specific values for those variables depending on the struct which has been instantiated. So I'd like something like:

myStruct* myStrPtr;
myStruct2* myStrPtr;

myStrPtr = initializeStruct();
myStrPtr->printContent //prints for example 55

myStrPtr2 = initializeStruct();
myStrPtr2->printContent //prints for example 6548

example

typedef struct {

    void (*sum)(int a, int b);
    void (*printContent)(void);

    int content;

}myStruct;

void printInhoud(void){
   printf("content: %d\n", content);}

void sum(int a, int b){

 /***********THIS DOESN T WORK OBVIOUSLY************/
    this->content = a+b;
    printf("calculated sum: %d", sum);
  }


myStruct * initializeStruct(void)
{
    myStruct* myStrPtr = malloc(sizeof(myStruct));
    myStrPtr -> foo = foo ; 
    mynStrPtr->printContent = printContent;

    return myStrPtr;
}

 void freeMem(myStruct * myStructPtr)
{
   free(myStructPtr);
}


int main (void)
{
    int a= 1;
    int b=33;

    myStruct* myStrPtr;

    myStrPtr = initializeStruct();

    myStrPtr->printContent();

    return 0;
}

2) my second question is: what are pro's and con's about programming in this way? I think that if I am able to add the variables in the structures and access them just like in OOP, I get a big advantage: modularity. I am active in embedded software and believe that having such an advantage of OOP on an embedded system can be very interesting. I have been criticized for trying to do this. The only reason I was given: "You have no garbage collector, so don't". Could someone give me some pro's and con's and explain why this would be "such an incredibly bad programming practice"

  • 2
    "Object Oriented C" is a subset of C++ – CinCout Feb 15 '16 at 10:36
  • @HappyCoder How could I change my title to make it more clear, what I mean? – LandonZeKepitelOfGreytBritn Feb 15 '16 at 10:38
  • Honestly, I have no clue! @trilolil – CinCout Feb 15 '16 at 10:41
  • 1
    @HappyCoder and much,much more! – David Haim Feb 15 '16 at 10:41
  • 3
    As a developer who had to maintain a huge codebase of which a part was written in "object-oriented C", along with inheritance, vtable-like structures... please don't do this :) It is litterally impossible to debug, because of the necessity to maintain pointers as opaque types. C++ exists for a reason, and it is exactly this. (Pun intended, the `this` pointer makes things much easier). – SirDarius Feb 15 '16 at 10:42
  • void sum(int a, int b){printf("calculated sum: %d", sum); printf("calculated sum: %d", sum); - it is from pascal? Pascal have special variable named as function for return value. – oklas Feb 15 '16 at 10:42
  • @DavidHaim, please explain what you mean. I asked this question like 2 minuts ago got "plenty" of views in a very short time and already got downvoted twice. – LandonZeKepitelOfGreytBritn Feb 15 '16 at 10:42
  • @SirDarius, but by writing it in this way aren't you able to have the advantages of OOP on an embedded platform? – LandonZeKepitelOfGreytBritn Feb 15 '16 at 10:44
  • ^You can use C++ on embedded platforms – CinCout Feb 15 '16 at 10:47
  • @trilolil But the "advantages", whatever they may be, might be far outweighed by the disadvantages of using OOP in a language that doesn't support it. Also, OOP is not the solution to all problems. I would strongly suggest against using OOP in a language that doesn't offer good support for it. – juanchopanza Feb 15 '16 at 10:48
  • You get some slight advantages, but TONS of disadvantages - like manually having to handle *everything* yourself. Using C++ you would get construction and destruction and virtual functions *automatically*, plus the `this` pointer, plus lots and lots of ready made classes. Sure, you can do this, or you can write OOP assembly. But it is **hard** and there is another language that has already solved all your problems, and more. – Bo Persson Feb 15 '16 at 10:48
  • @HappyCoder, I hope you aren't thinking about arduino.... – LandonZeKepitelOfGreytBritn Feb 15 '16 at 10:48
  • 1
    The advantages of OOP are mainly encapsulation and reuse of code. Unreadable and unmaintainable code is an unacceptable trade-off. Using structs and free functions with "this-like" pointers as arguments is the closest to OOP you want to be. Just have a look at the SQLite library. It's C code, but easy to read and understand, is used universally on embedded platforms, and yet makes no use of "advanced" OOP techniques. – SirDarius Feb 15 '16 at 10:48
  • Related interesting watch: https://www.youtube.com/watch?v=TYqbgvHfxjM – CinCout Feb 15 '16 at 10:59

3 Answers3

1

You can do OOP in C, but one of the major cons is that you have to pass the this pointer around. Let's say you have a pointer to a printInfo() function in your struct and you want to print the info of that particular struct. You have to do

my_struct->printInfo(my_struct);

or, if my_struct is not a pointer to your struct

my_struct.printInfo(&my_struct);

There's no way around it.

For your second question, I'm not sure doing OOP in C is really practical. I have done it out of curiosity and it's really fun. You can get inheritance, virtual base classes, polymorphism and all. If you're interested you can check it out here:

https://github.com/vladcc/object-oriented-c

Vlad Dinev
  • 432
  • 2
  • 9
0

Not only encapsulation, many other OOP concepts can be implemented using C with little bit effort.

Here is an example.

//
// cobj.h
//

#ifndef __COBJ_H__
#define __COBJ_H__
struct _cobj_priv;
typedef struct _cobj {
    struct _cobj_priv *priv;

    void (*set_data)(struct _cobj *obj, int data);
    int (*get_data)(struct _cobj *obj);
    void (*print_data)(struct _cobj *obj);
} cobj_t;

cobj_t *new_struct(void);
void free_struct(cobj_t *obj);

#endif /* __COBJ_H__ */

//
// cobj.c
//

#include "cobj.h"
#include <stdio.h>
#include <stdlib.h>

//
// Internal section
//
struct _cobj_priv {
    int data;
};

static void set_data (struct _cobj *obj, int data) {
    struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;

    if (this) {
        this->data = data;
    }
}

static int get_data (struct _cobj *obj) {
    struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;

    return (this)? this->data : 0;
}

static void print_data (struct _cobj *obj) {
    struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;

    if (this)
        printf("%d\n", this->data);
}

//
// APIs section
//
cobj_t *new_struct(void) {
    cobj_t *obj = malloc(sizeof(cobj_t));

    if (obj) {
        obj->priv = malloc(sizeof(struct _cobj_priv));

        if (obj->priv) {
            obj->priv->data = 0;
        }

        obj->set_data = &set_data;
        obj->get_data = &get_data;
        obj->print_data = &print_data;
    }

    return obj;
}

void free_struct(cobj_t *obj) {
    if (obj) {
        if (obj->priv)
            free(obj->priv);

        free(obj);

        obj = null;
    }
}

//
// main.c
//

#include "cobj.h"
#include <stdio.h>

int main(int argc, char *argv[]) {
    cobj_t *obj = new_struct();

    if (obj) {
        obj->print_data(obj);
        obj->set_data(obj, 100);
        obj->print_data(obj);

        printf("> get data return %d\n", obj->get_data(obj));
    }

    return 0;
}

Result:

0
100
> get data return 100

In C, struct's methods are function pointers, they do not know about the existence of struct so that they can not access to the struct members. You need to pass the struct instance to methods as a parameter in order to access to its members.

S Dao
  • 555
  • 4
  • 7
  • 1
    *"In C, the object's members can be completely hidden from the user view, not in C++."* What makes you think that same couldn't done in C++? – user694733 Feb 15 '16 at 11:54
  • In C++, once end users have the header file, they can see the class members. In C, using forward declaration, end users see only `struct abc;` not `abc`'s members. So in C you CAN NOT see the struct members, in C++ you CAN see the class's members but inaccessible in case of private. – S Dao Feb 15 '16 at 11:59
  • And you couldn't use forward declaration in C++? Pointer to inner class equally possible in C++ as pointer to inner struct is in C. – user694733 Feb 15 '16 at 12:03
  • But you must declare inner class before use right? Where do you suppose to put the inner class's declaration in, header file or source file? – S Dao Feb 15 '16 at 12:27
  • Forward declaration in header, and full declaration in source file. Just like you have done with `_cobj_priv`. [Example in Wikipedia](https://en.wikipedia.org/wiki/Opaque_pointer#C.2B.2B) – user694733 Feb 15 '16 at 12:28
  • Oh, I forgot that C++ uses `struct` keyword to define inner *class* :D. Ok, you can mix C++ and C as you want because `C++` = `C` + `++`. – S Dao Feb 15 '16 at 12:42
  • I doesn't matter if you use `struct` or `class` for inner class. You can use `public:` with `class` and it'll work just as well. – user694733 Feb 15 '16 at 12:48
  • Man, in C++ when you use *class* keyword to define an object, all the class's members should be disclosed to user. That's the point. And because in C++, the encapsulation can not be done completely(user still see the class'members) then they workaround by adding the keyword *private* to make class become a glass box instead of black box like C does with struct keyword. Even you use the inner class, the variable that has inner class type still visible to user. Back to the wiki page you shared, look at the declaration `unique_ptr d_ptr;`, you see, the `d_ptr` is visible. – S Dao Feb 16 '16 at 09:41
  • What is your point? `d_ptr` is visible, just like `struct _cobj_priv *priv` is visible on your example. Both are just pointers. Only difference is that `d_ptr` is private and thus not accesible to outside, while `priv` is public. – user694733 Feb 16 '16 at 09:54
  • `priv` is public because I want it to be in this case for some reasons. Now example if I change the `void (*set_data)(struct _cobj *obj, int data);` to `void (*set_data)(struct _cobj_priv*obj, int data);`. You can see that the declaration of `_cobj_priv` is invisible. With *class* keyword you can not do that without putting the declaration of class to a header file. Please watch [this](https://www.youtube.com/watch?v=QHnLmvDxGTY) video (from 00:38:00) if my explanation does not satisfy you. – S Dao Feb 16 '16 at 11:36
  • You are not understanding what I am saying. **I am not talking about using private keyword to achieve encapsulation, I am saying that you can do use PIMPL idiom in C and C++ equally well.** Uncle Bob compared different ways of doing encapsulation. He did not say C has better encapsulation than C++, he said that C++ access specifiers were not optimal solution to the problem. PIMPL will work on either language. Whether you use class or struct keyword for internal class, is [irrelevant](https://ideone.com/c1RgM7). – user694733 Feb 16 '16 at 15:22
0

Disclaimer: I'm not sure that the following is truely an answer, but it is way too long for a comment.

Is it possible to do OOP in C?

Yes it is. First C++ compilers were mere pre-processors that converted C++ source to C. Of course, you have neither contructors not destructors so you must explicitely call them, you must use composition pattern for inheritance, you must have vtables for virtual methods and must explicitely pass the this pointer.

Simple example with ctor, dtor, a dynamic array and a method:

struct __foo {
    int *arr;
    int n;
};
typedef struct __foo foo;
bool init_foo(foo *f, int n) {
    f->arr = malloc(n * sizeof(int));
    f->n = n;
    return (f->arr != NULL);
}
void clean_foo(foo *f) { free(f->arr); }
bool set(int index, int value, foo *f) {  // same for get...
    if ((index >= f->n) || (index < 0)) return false;
    f->arr[index] = value;
    return true;
}

Usage:

foo f;
init_foo(8, &f);
set(5, 2, &f);
clean_foo(&f);

More complex example with inheritance and virtual method :

typedef struct {
    int age;
    const char *name;
    const char* (*say)(void *);
} animal;
typedef struct {
    animal parent;
} dog;
typedef struct {
    animal parent;
} cat;
void init_animal(int age, const char *name, animal *a) {
    a->age = age;
    a->name = name;
}
char *pre_say(animal *this) {
    char * msg = malloc(strlen(this->name) + 11);
    strcpy(msg, this->name);
    strcat(msg, " says ");
    return msg;

const char * wow(void *an) {
    animal *this = (animal *) an;
    char * msg = pre_say(this);
    strcat(msg, "Woof");
    return msg;
}
const char* meeow(void *an) {
    animal *this = (animal *) an;
    char * msg = pre_say(this);
    strcat(msg, "Meew");
    return msg;
}
void init_dog(int age, const char * name, dog *d) {
    init_animal(age, name, &(d->parent));
    d->say = &woof;
}
void init_cat(int age, const char * name, cat *c) {
    init_animal(age, name, &(c->parent));
    d->say = &meeow;
}

Usage example:

dog d;
init_dog(2, "Rintintin", &d);
cat c;
init_cat(3, "Tom", &c);
const char *msg = (d.say)(&d);  // msg <- Rintintin says Woof
free(msg);
msg = (c.say)(&c);    // msg <- Tom says Meew
free(msg);

Should we do OOP in C?

Definitely NO. As you can say from previous example, as soon as you need polymorphism, pointers must be void * and you loose all possible type checking. And you also loose all the C++ goodies of automatic construtors and destructors, automatic this pointer, etc. You end with much more code, much harder to read and debug. The only acceptable use cases (IMHO) are:

  • interfacing C++ and C
  • minimal conversion of C++ code for a platform where you have only a C compiler.
  • low level codes (kernel or high performance libraries) in which you do not want to be bored with the overhead of C++, and still need OOP.

And just remember: if C++ was invented, it must have been to fill a gap in C language. So don't try to do what C++ was created for in C, unless you really know why you do that.

Has Garbage Collector something to do with OOP?

Here again no. C++ has no garbage collection and is indeed an OO language.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • C++ is a multi-paradigm language. It supports OOP to some extent. Using OOP in C is possible, even if it's just used barely-known, unimportant software projects like Linux, that that in spite of the language offering little support for it. – Ulrich Eckhardt Feb 15 '16 at 13:03
  • @UlrichEckhardt: Looks like I was forgotting something :-( . But I would not advise a still young C student to do C OOP when writing an end user application. – Serge Ballesta Feb 15 '16 at 13:14
  • Actually it's pretty hard to tell what's right in this case, giving the limited info. Another influence might be the embedded platform the code is supposed to run on, although C++ on bare iron environments isn't that unusual nowadays. – Ulrich Eckhardt Feb 15 '16 at 13:16