Hoppa till innehåll

Object Oriented development in ANSI C?

Even though I once started with, and for a long time developed with, procedural languages, over the years I have started to think much more object oriented when I develop. It started when I learned C ++ and has become an intuitive way for me to see systems and solutions to my development problems. A while ago, however, I found myself in the situation of being limited to ANSI C. The code was to be compiled on gcc and thus new, class and the like are just to be forgotten. Much of the code in place was written purely procedurally, but that part tried to encapsulate different parts by grouping them in different files and so on. It also clearly struggled with many of the large scale systems issues that C++ aimed to solve.

I also found it surprisingly difficult to completely let go of the object-oriented way of thinking and grew interested in exploring to what degree many of the object oriented ideas could be used in plain ANSI C. This is the first the time I do this, so I learn and come up with new things all the time and a lot will probably be revised. But here are some thoughts.

Inheritance

Inheritance can to some extent be faked by your structs looking identical on the parts they have in common and that all specific parts come last in the structure.

struct foo {
  int a;
  int b;
}

struct bar {
  int a;
  int b;
  char* s;
}

void printStruct(struct foo* obj) {
  fprintf (stderr, "a:%s \nb:%s \n", obj->a, obj->b);
  return;
}

printStruct will now be able to receive both foo and bar structs. But the downside is that you have to type your bar struct yourself if you want to submit it to the function. The function can also not print the string in a bar struct as it would be a problem if you had in fact sent a foo struct. One way to get around this is to let the function take a void* instead, but then you can really send it anything and it is impossible to know which struct you are really receiving. A “solution” may be to have a type of field in your struct. You can then let the function check what type it is and choose what it should do with the struct based on that.

enum StructType {FOO, BAR};

struct foo {
  structType type;
  int a;
  int b;
}

struct bar {
  structType type;
  int a;
  int b;
  char* s;
}

void printStruct (void* obj) {
  // Type throw to the "lowest" type and check the type field
  switch (((foo*)obj)->type) {
  case FOO:
    fprintf (stderr, "a:%s \nb:%s \n", ((foo *)obj)->a, ((foo*)obj)->b);
    break;
  case BAR:
    fprintf (stderr, "a:%s \nb:%s \ns:%s \n", ((foo*)obj)->a, ((foo*)obj)->b, ((foo*)obj)->s);
    break;
  default:
    fprintf (stderr, "UNKOWN TYPE! \n");
    break;
  }

  return;
}

Now the function will print the string if it is a bar struct you submit. Of course, type must be set to the correct value, something that could be managed via “new”-functions that create the structs. For example

struct foo* MyFoo = NewFoo(aVal, bval);

I have heard rumors that you should be able to define your struct in such a way that you could actually let the printStruct function take a pointer to the “base” struct, but this is not something I have succeeded with. If anyone knows, feel free to shout.

Private variables

Normally in object oriented programming we end up having a lot of variables in our objects that we do not really want the end user to touch. In C++ we declare these as private (or protected). But in a struct in ANSI C, this possibility does not exist. However, what you can do is fake this to an extent. In you .h file you declare the struct with something like

typedef struct MyObj MyObj;

and then in you .c file you write the actual definition

struct MyObj {
  int a;
  int b;
}

Now all your functions in your .c file for the object will know what your struct looks like inside, but for everyone else who only has the declaration in your .h file to go on, the member variables will be “hidden”. We have now managed to recreate some of the possibilities with private variables. If you want a static private member variable, you achieve similar functionality by declaring it outside your struct in your .c file.

Member functions

Member functions are a big part of object oriented programming. Let’s say you for instance, have an array of pointers to your objects, all of which can now be of a slightly different type. But we know that everyone should execute a “run”-function. However, not every object should do the exact same thing. Here we can do as before to check a type of variable in our struct and based on that choose what we are going to do. Something that is nicer, however, is to use member functions. Something that can be achieved with ANSI C.

typedef int funcPtr ();
typedef struct myObj {
  int a;
  int b;
  funcPtr* run;
} myObj;

typedef struct myOtherObj {
  int a;
  int b;
  funcPtr* run;
  char* s;
} myOtherObj;

int runOne () {
  return 1;
}

int runTwo () {
  return 2;
}

In your “new”-functions for the different “objects” you assign the functions.

struct MyObj* NewMyObj (int aVal, int bVal) {
  struct MyObj* this = (stuct MyObj*) malloc (sizeof (struct MyObj));
  this->a   = aVal;
  this->b   = bVal;
  this->run = runOne;
  return this;
}

Then you can simply call the function on your “object”.

struct MyObj* myObj = NewMyObj (10, 20);
int result = myObj->run();

The result will now be 1 when we assigned runOne to our “run”-function in the constructor and that function returns 1. If this object were part of an array of objects we could have looped them and run their respective “run”-functions and each of the objects could in turn point to completely different implementations of that function.

These were just a few quick things I’ve come up with in recent days. Feel free to share tips and ideas. Please note that the code above has not been compiled there may be some small errors here and there, but the idea itself should be clear.

Kommentarer

Dela i ditt aktivitets flöde

Powered by WP LinkPress

Kommentarer

Dela i ditt aktivitets flöde

Powered by WP LinkPress