Skip to content
/ obj.h Public

πŸš€ OOP in pure C with a single-header

License

Notifications You must be signed in to change notification settings

small-c/obj.h

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

obj.h

A single-header supports OOP in pure C.
Using power of preprocessor and hacking on assembly to unlock the limits.

Foo f = new(Foo)(10);       // create Foo instance
assert(f->get() == 10);     // get value
f->base.release();          // release

Features

  • C /Java inspired OOP
    • Public, private members
    • Constructor, destructor
    • Abstraction
    • Inheritance
  • Zero dependency
  • No breaking editor's intellisense
  • x86/_64 target
  • arm target

Usage

  • Just add obj.h to your C source
  • See tests for more

C comparison:

// C   native OOP               // C with obj.h
class Foo {                 |   class(Foo, public(
public:                     |       int (*get)();
    Foo(int bar);           |   ), private(
    int get();              |       int bar;
private:                    |   ));
    int bar;                |
};                           |   ctor(Foo)(int bar) {
                            |       obj_setup(Foo);
Foo::Foo(int bar) {         |       obj_bind(Foo, get);
    this->bar = bar;        |       self->bar = bar;
}                           |       obj_done(Foo);
                            |   }
int Foo::get() {            |
    return this->bar;       |   method(Foo, int, get)() {
}                           |       obj_prepare(Foo);
                            |       return self->bar;
                            |   }
                            |
Foo *f = new Foo(15);       |   Foo f = new(Foo)(15);
f->get();                   |   f->get();
delete f;                   |   f->base.release();

Platform support:

GCC 4 MSVC 14 Clang 5 TCC 0.9
Windows (x86 / x64) βœ… βœ… βœ… βœ…
Linux (i386 / x86_x64) βœ… _ βœ… βœ…
Mac OSX (i386 / x86_64) βœ… _ βœ… _

How it works?

We can't explain in detail, but something like binding this to a function in JavaScript.

Simulate a simple class with struct:

struct A {
    void (* todo)();  // method
};

And we have a static function:

static void fn_todo() {}

Next, bind A instance (aka this) to fn_todo πŸ™„

binded_todo = bind(fn_todo, myA);

Finally 😎

myA->todo = binded_todo;
myA->todo();    // call it like a method

Closure function?

  • This is a fork of yulon/clofn
  • Just copy function header and inject some code to pre-allocate this inside
  • Currently, support x86 and x86_64 only

Function template:

static void fn_todo() {
    volatile size_t self = ...;
    ...

Disassemble:

; prolog
    mov     rax, ...
    mov     QWORD PTR [rbp-8], rax
    ...

Generated function:

 [copied prolog]
> x86
| mov     eax, [data]
| jmp     [addr]
> x86_64
| mov     rax, [data]
| push    rax
| mov     rax, [addr]
| jmp     rax

Refs:

How we provide classes, public/private?

See obj.h for more.


ko-fi