How do you implement a class in C?

CClassOopEmbedded

C Problem Overview


Assuming I have to use C (no C++ or object oriented compilers) and I don't have dynamic memory allocation, what are some techniques I can use to implement a class, or a good approximation of a class? Is it always a good idea to isolate the "class" to a separate file? Assume that we can preallocate the memory by assuming a fixed number of instances, or even defining the reference to each object as a constant before compile time. Feel free to make assumptions about which OOP concept I will need to implement (it will vary) and suggest the best method for each.

Restrictions:

  • I have to use C and not an OOP because I'm writing code for an embedded system, and the compiler and preexisting code base is in C.
  • There is no dynamic memory allocation because we don't have enough memory to reasonably assume we won't run out if we start dynamically allocating it.
  • The compilers we work with have no problems with function pointers

C Solutions


Solution 1 - C

That depends on the exact "object-oriented" feature-set you want to have. If you need stuff like overloading and/or virtual methods, you probably need to include function pointers in structures:

typedef struct {
  float (*computeArea)(const ShapeClass *shape);
} ShapeClass;

float shape_computeArea(const ShapeClass *shape)
{
  return shape->computeArea(shape);
}

This would let you implement a class, by "inheriting" the base class, and implementing a suitable function:

typedef struct {
  ShapeClass shape;
  float width, height;
} RectangleClass;

static float rectangle_computeArea(const ShapeClass *shape)
{
  const RectangleClass *rect = (const RectangleClass *) shape;
  return rect->width * rect->height;
}

This of course requires you to also implement a constructor, that makes sure the function pointer is properly set up. Normally you'd dynamically allocate memory for the instance, but you can let the caller do that, too:

void rectangle_new(RectangleClass *rect)
{
  rect->width = rect->height = 0.f;
  rect->shape.computeArea = rectangle_computeArea;
}

If you want several different constructors, you will have to "decorate" the function names, you can't have more than one rectangle_new() function:

void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
  rectangle_new(rect);
  rect->width = width;
  rect->height = height;
}

Here's a basic example showing usage:

int main(void)
{
  RectangleClass r1;

  rectangle_new_with_lengths(&r1, 4.f, 5.f);
  printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
  return 0;
}

I hope this gives you some ideas, at least. For a successful and rich object-oriented framework in C, look into glib's GObject library.

Also note that there's no explicit "class" being modelled above, each object has its own method pointers which is a bit more flexible than you'd typically find in C++. Also, it costs memory. You could get away from that by stuffing the method pointers in a class structure, and invent a way for each object instance to reference a class.

Solution 2 - C

I had to do it once too for a homework. I followed this approach:

  1. Define your data members in a struct.
  2. Define your function members that take a pointer to your struct as first argument.
  3. Do these in one header & one c. Header for struct definition & function declarations, c for implementations.

A simple example would be this:

/// Queue.h
struct Queue
{
    /// members
}
typedef struct Queue Queue;

void push(Queue* q, int element);
void pop(Queue* q);
// etc.
/// 

Solution 3 - C

If you only want one class, use an array of structs as the "objects" data and pass pointers to them to the "member" functions. You can use typedef struct _whatever Whatever before declaring struct _whatever to hide the implementation from client code. There's no difference between such an "object" and the C standard library FILE object.

If you want more than one class with inheritance and virtual functions, then it's common to have pointers to the functions as members of the struct, or a shared pointer to a table of virtual functions. The GObject library uses both this and the typedef trick, and is widely used.

There's also a book on techniques for this available online - Object Oriented Programming with ANSI C.

Solution 4 - C

C Interfaces and Implementations: Techniques for Creating Reusable Software, David R. Hanson

><http://www.informit.com/store/product.aspx?isbn=0201498413>

This book does an excellent job of covering your question. It's in the Addison Wesley Professional Computing series.

The basic paradigm is something like this:

/* for data structure foo */

FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);

Solution 5 - C

you can take a look at GOBject. it's an OS library that give you a verbose way to do an object.

http://library.gnome.org/devel/gobject/stable/

Solution 6 - C

I will give a simple example of how OOP should be done in C. I realise this thead is from 2009 but would like to add this anyway.

/// Object.h
typedef struct Object {
    uuid_t uuid;
} Object;

int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);

/// Person.h
typedef struct Person {
    Object obj;
    char *name;
} Person;

int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);

/// Object.c
#include "object.h"

int Object_init(Object *self)
{
    self->uuid = uuid_new();

    return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
    return self->uuid;
}
int Object_clean(Object *self)
{
    uuid_free(self->uuid);

    return 0;
}

/// Person.c
#include "person.h"

int Person_init(Person *self, char *name)
{
    Object_init(&self->obj); // Or just Object_init(&self);
    self->name = strdup(name);

    return 0;
}
int Person_greet(Person *self)
{
    printf("Hello, %s", self->name);

    return 0;
}
int Person_clean(Person *self)
{
    free(self->name);
    Object_clean(self);

    return 0;
}

/// main.c
int main(void)
{
    Person p;

    Person_init(&p, "John");
    Person_greet(&p);
    Object_get_uuid(&p); // Inherited function
    Person_clean(&p);

    return 0;
}

The basic concept involves placing the 'inherited class' at the top of the struct. This way, accessing the first 4 bytes in the struct also accesses the first 4 bytes in the 'inherited class' (Asuming non-crazy optimalisations). Now, when the pointer of the struct is cast to the 'inherited class', the 'inherited class' can access the 'inherited values' in the same way it would access it's members normally.

This and some naming conventions for constructors, destructors, allocation and deallocarion functions (I recommend init, clean, new, free) will get you a long way.

As for Virtual functions, use function pointers in the struct, possibly with Class_func(...); wrapper too. As for (simple) templates, add a size_t parameter to determine size, require a void* pointer, or require a 'class' type with just the functionality you care about. (e.g. int GetUUID(Object *self); GetUUID(&p);)

Solution 7 - C

Use a struct to simulate the data members of a class. In terms of method scope you can simulate private methods by placing the private function prototypes in the .c file and the public functions in the .h file.

Solution 8 - C

GTK is built entirely on C and it uses many OOP concepts. I have read through the source code of GTK and it is pretty impressive, and definitely easier to read. The basic concept is that each "class" is simply a struct, and associated static functions. The static functions all accept the "instance" struct as a parameter, do whatever then need, and return results if necessary. For Example, you may have a function "GetPosition(CircleStruct obj)". The function would simply dig through the struct, extract the position numbers, probably build a new PositionStruct object, stick the x and y in the new PositionStruct, and return it. GTK even implements inheritance this way by embedding structs inside structs. pretty clever.

Solution 9 - C

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>

/**
 * Define Shape class
 */
typedef struct Shape Shape;
struct Shape {
    /**
     * Variables header...
     */
    double width, height;

    /**
     * Functions header...
     */
    double (*area)(Shape *shape);
};

/**
 * Functions
 */
double calc(Shape *shape) {
        return shape->width * shape->height;
}

/**
 * Constructor
 */
Shape _Shape() {
	Shape s;

	s.width = 1;
    s.height = 1;

    s.area = calc;

    return s;
}

/********************************************/

int main() {
	Shape s1 = _Shape();
	s1.width = 5.35;
    s1.height = 12.5462;

    printf("Hello World\n\n");

    printf("User.width = %f\n", s1.width);
    printf("User.height = %f\n", s1.height);
    printf("User.area = %f\n\n", s1.area(&s1));

    printf("Made with \xe2\x99\xa5 \n");

    return 0;
};

Solution 10 - C

In your case the good approximation of the class could be the an ADT. But still it won't be the same.

Solution 11 - C

My strategy is:

  • Define all code for the class in a separate file
  • Define all interfaces for the class in a separate header file
  • All member functions take a "ClassHandle" which stands in for the instance name (instead of o.foo(), call foo(oHandle)
  • The constructor is replaced with a function void ClassInit(ClassHandle h, int x, int y,...) OR ClassHandle ClassInit(int x, int y,...) depending on the memory allocation strategy
  • All member variables are store as a member of a static struct in the class file, encapsulating it in the file, preventing outside files from accessing it
  • The objects are stored in an array of the static struct above, with predefined handles (visible in the interface) or a fixed limit of objects that can be instantiated
  • If useful, the class can contain public functions that will loop through the array and call the functions of all the instantiated objects (RunAll() calls each Run(oHandle)
  • A Deinit(ClassHandle h) function frees the allocated memory (array index) in the dynamic allocation strategy

Does anyone see any problems, holes, potential pitfalls or hidden benefits/drawbacks to either variation of this approach? If I am reinventing a design method (and I assume I must be), can you point me to the name of it?

Solution 12 - C

Also see this answer and this one

It is possible. It always seems like a good idea at the time but afterwards it becomes a maintenance nightmare. Your code become littered with pieces of code tying everything together. A new programmer will have lots of problems reading and understanding the code if you use function pointers since it will not be obvious what functions is called.

Data hiding with get/set functions is easy to implement in C but stop there. I have seen multiple attempts at this in the embedded environment and in the end it is always a maintenance problem.

Since you all ready have maintenance issues I would steer clear.

Solution 13 - C

My approach would be to move the struct and all primarily-associated functions to a separate source file(s) so that it can be used "portably".

Depending on your compiler, you might be able to include functions into the struct, but that's a very compiler-specific extension, and has nothing to do with the last version of the standard I routinely used :)

Solution 14 - C

The first c++ compiler actually was a preprocessor which translated the C++ code into C.

So it's very possible to have classes in C. You might try and dig up an old C++ preprocessor and see what kind of solutions it creates.

Solution 15 - C

Do you want virtual methods?

If not then you just define a set of function pointers in the struct itself. If you assign all the function pointers to standard C functions then you will be able to call functions from C in very similar syntax to how you would under C++.

If you want to have virtual methods it gets more complicated. Basically you will need to implement your own VTable to each struct and assign function pointers to the VTable depending on which function is called. You would then need a set of function pointers in the struct itself that in turn call the function pointer in the VTable. This is, essentially, what C++ does.

TBH though ... if you want the latter then you are probably better off just finding a C++ compiler you can use and re-compiling the project. I have never understood the obsession with C++ not being usable in embedded. I've used it many a time and it works is fast and doesn't have memory problems. Sure you have to be a bit more careful about what you do but its really not that complicated.

Solution 16 - C

C isn't an OOP language, as your rightly point out, so there's no built-in way to write a true class. You're best bet is to look at structs, and function pointers, these will let you build an approximation of a class. However, as C is procedural you might want to consider writing more C-like code (i.e. without trying to use classes).

Also, if you can use C, you can probally use C++ and get classes.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBen GartnerView Question on Stackoverflow
Solution 1 - CunwindView Answer on Stackoverflow
Solution 2 - CerelenderView Answer on Stackoverflow
Solution 3 - CPete KirkhamView Answer on Stackoverflow
Solution 4 - CMark HarrisonView Answer on Stackoverflow
Solution 5 - CAlexView Answer on Stackoverflow
Solution 6 - CyynyView Answer on Stackoverflow
Solution 7 - CTaylor LeeseView Answer on Stackoverflow
Solution 8 - CrocketsarefastView Answer on Stackoverflow
Solution 9 - CPieroziView Answer on Stackoverflow
Solution 10 - CArtem BargerView Answer on Stackoverflow
Solution 11 - CBen GartnerView Answer on Stackoverflow
Solution 12 - CGerhardView Answer on Stackoverflow
Solution 13 - CwarrenView Answer on Stackoverflow
Solution 14 - CToadView Answer on Stackoverflow
Solution 15 - CGozView Answer on Stackoverflow
Solution 16 - Cuser130076View Answer on Stackoverflow