Discussion:
hyper experimental vtable attempt...
(too old to reply)
Chris M. Thomasson
2017-07-25 07:25:04 UTC
Permalink
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
functionality. The code can be found here:

https://pastebin.com/raw/QPssvGJR
(pastebin raw, without ads... :)

And gives the following output for me:
______________________
(000000000060FE30:ct_person_create):(name:Chris M. Thomasson)
(000000000060FE30:ct_employee_print):(id:103)
(000000000060FE30:ct_person_print):(name:Chris M. Thomasson)
(000000000060FE30:ct_employee_destroy):(id:103)
(000000000060FE30:ct_person_destroy):(name:Chris M. Thomasson)

_______________________________________
(000000000060FE30:ct_person_create):(name:Jane Doe)
(000000000060FE30:ct_employee_print):(id:1203)
(000000000060FE30:ct_person_print):(name:Jane Doe)
(000000000060FE30:ct_employee_destroy):(id:1203)
(000000000060FE30:ct_person_destroy):(name:Jane Doe)
______________________


Afaict, this might have the possibility to be "somewhat" useful?

Ummm.. Well, take a look at lines 17-24:
______________________
#define ct_object_vtable(mp_self) \
((((struct ct_object_vtable*)(*((void**)(mp_self)))))

#define ct_object_print(mp_self, mp_file) \
(ct_object_vtable(mp_self)->fp_print((mp_self), (mp_file))))

#define ct_object_destroy(mp_self) \
(ct_object_vtable(mp_self)->fp_destroy((mp_self))))
______________________


Ummm.... Is that kosher? Damn...

The code for main is:
_____________________
int main(void)
{
struct ct_employee employee;

if (!ct_employee_create(&employee, "Chris M. Thomasson", 103))
{
struct ct_person* const person = &employee.m_person;

ct_object_print(&employee, stdout);
ct_object_destroy(person);
}

printf("\n_______________________________________\n");

if (!ct_employee_create(&employee, "Jane Doe", 1203))
{
struct ct_person* const person = &employee.m_person;

ct_object_print(person, stdout);
ct_object_destroy(person);
}

return 0;
}
_____________________


Afaict, the overall result has a little OOP spice about it? Humm...

Will have more time tomorrow to drill down on its details; sorry about
that. ;^o
bartc
2017-07-25 11:38:47 UTC
Permalink
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
https://pastebin.com/raw/QPssvGJR
If that's OO in C then it's unreadable. It would be easier doing one of
those word-search puzzles to locate words within a grid of letters than
trying to figure out what's happening. But those at least are fun...
Post by Chris M. Thomasson
(pastebin raw, without ads... :)
(I wonder why no one else has thought of using a raw link?)

I tried the example (or as close to its functionality as I could see) in
my little language which has hardly any OO features to speak of (and
those are buggy).

If I'm going to use OO, I'd want it to look like this:

https://pastebin.com/raw/8XTbj2bk
--
bartc
Thiago Adams
2017-07-25 12:45:39 UTC
Permalink
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
Instead of explaining what I would do differently, I did two samples.

Sample 1 - Interface implementation
-----------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Shape {
void (*Draw)(struct Shape *pShape);
void (*Destroy)(struct Shape *pShape);
} Shape;

inline void Shape_Draw(Shape *pShape) { pShape->Draw(pShape); }
inline void Shape_Destroy(Shape *pShape) { pShape->Destroy(pShape); }

typedef struct Box { Shape *pShape; } Box;

Box *Box_Create();
void Box_Destroy(Box *pBox);
void Box_Draw(Box *pBox);

Box *Box_Create() {
Box *pBox = malloc(sizeof *pBox);
if (pBox) {
static Shape vtable = {(void *)(Shape *)Box_Draw,
(void *)(Shape *)Box_Destroy};
pBox->pShape = &vtable;
}
return pBox;
}

inline Shape *Box_As_Shape(Box *pBox) { return pBox->pShape; }

void Box_Draw(Box *pBox) {}
void Box_Destroy(Box *pBox) {}

typedef struct Circle { Shape *pShape; } Circle;

Circle *Circle_Create();
void Circle_Destroy(Circle *pCircle);
void Circle_Draw(Circle *pCircle);

Circle *Circle_Create() {
Circle *pCircle = malloc(sizeof *pCircle);
if (pCircle) {
static Shape vtable = {(void *)(Shape *)Circle_Draw,
(void *)(Shape *)Circle_Destroy};
pCircle->pShape = &vtable;
}
return pCircle;
}
void Circle_Draw(Circle *pCircle) {}
void Circle_Destroy(Circle *pCircle) {}

inline Shape *Circle_As_Shape(Circle *pCircle) { return pCircle->pShape; }

int main(void) {
Shape *pShape;

Box *pBox = Box_Create();
pShape = Box_As_Shape(pBox);
Shape_Draw(pShape);
return 0;
}
------------------

Sample 2 - Union Types ( a type can be Box or Circle)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Shape { int type; } Shape;

typedef struct Box { int type; } Box;

Box *Box_Create();
void Box_Destroy(Box *pBox);
void Box_Draw(Box *pBox);

Box *Box_Create() {
Box *pBox = malloc(sizeof *pBox);
if (pBox) {
pBox->type = 1;
}
return pBox;
}

inline Shape *Box_As_Shape(Box *pBox) { return pBox; }

void Box_Draw(Box *pBox) {}
void Box_Destroy(Box *pBox) {}

typedef struct Circle { int type; } Circle;

Circle *Circle_Create();
void Circle_Destroy(Circle *pCircle);
void Circle_Draw(Circle *pCircle);

Circle *Circle_Create() {
Circle *pCircle = malloc(sizeof *pCircle);
if (pCircle) {
pCircle->type = 2;
}
return pCircle;
}
void Circle_Draw(Circle *pCircle) {}
void Circle_Destroy(Circle *pCircle) {}

inline Shape *Circle_As_Shape(Circle *pCircle) { return pCircle; }

void Shape_Draw(Shape *pShape) {
switch (pShape->type) {
case 1:
Box_Draw((Box *)pShape);
break;
case 2:
Circle_Draw((Circle *)pShape);
break;
}
}

void Shape_Destroy(Shape *pShape) {
switch (pShape->type) {
case 1:
Box_Destroy((Box *)pShape);
break;
case 2:
Circle_Destroy((Circle *)pShape);
break;
}
}

int main(void) {
Shape *pShape;

Box *pBox = Box_Create();
pShape = Box_As_Shape(pBox);
Shape_Draw(pShape);
return 0;
}
--------------

Abstractions are build around types and functions. (See C' topic)

The sample 1 is what I call "open polymorphism".
I don't need to know the types that implement Shape.
Box and Circle implements the Shape interface.

The sample 2 is what I call "closed polymorphism".
I need to know the types. Shape is Box or Circle.

Both types of polymorphism are useful.

Note that the main function and Box_Draw didn't change between samples.
The functions declarations are the same.
Thiago Adams
2017-07-25 12:58:24 UTC
Permalink
Post by Thiago Adams
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
Instead of explaining what I would do differently, I did two samples.
Fix for sample 1:

//(same for Shape_Destroy)

inline void Shape_Draw(Shape* pShape) {
struct ShapeImp {
Shape* vtable;
}* p = (struct ShapeImp*) pShape;

p->vtable->Draw(pShape);
}

inline Shape* Box_As_Shape(Box* pBox)
{
return pBox;
}
Thiago Adams
2017-07-25 13:22:17 UTC
Permalink
Post by Thiago Adams
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
Instead of explaining what I would do differently, I did two samples.
[...]

Alternative fix:

typedef struct ShapeVtbl
{
void(*Draw)(void* pShape);
void(*Destroy)(void* pShape);
} *Shape;

inline void Shape_Draw(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Draw(pShape);
}

Box* Box_Create()
{
...
static struct ShapeVtbl vtable = { (void*)(void*) Box_Draw ,
(void*)(void*) Box_Destroy };
pBox->pShape = &vtable;
...
}

inline Shape* Box_As_Shape(Box* pBox){
return pBox;
}
bartc
2017-07-25 13:36:48 UTC
Permalink
Post by Thiago Adams
Post by Thiago Adams
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
Instead of explaining what I would do differently, I did two samples.
[...]
typedef struct ShapeVtbl
{
void(*Draw)(void* pShape);
void(*Destroy)(void* pShape);
} *Shape;
inline void Shape_Draw(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Draw(pShape);
}
Box* Box_Create()
{
...
static struct ShapeVtbl vtable = { (void*)(void*) Box_Draw ,
(void*)(void*) ?

You'll have to explain that one.
Thiago Adams
2017-07-25 13:46:12 UTC
Permalink
Post by bartc
Post by Thiago Adams
Post by Thiago Adams
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
Instead of explaining what I would do differently, I did two samples.
[...]
typedef struct ShapeVtbl
{
void(*Draw)(void* pShape);
void(*Destroy)(void* pShape);
} *Shape;
inline void Shape_Draw(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Draw(pShape);
}
Box* Box_Create()
{
...
static struct ShapeVtbl vtable = { (void*)(void*) Box_Draw ,
(void*)(void*) ?
You'll have to explain that one.
My intention is to cast
void Box_Draw(Box*)
to
(void (*)(void*))

hum..I see.

Fix number 2 for sample 1


typedef struct ShapeVtbl
{
void(*Draw)(void*);
void(*Destroy)(void*);
} *Shape;

inline void Shape_Draw(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Draw(pShape);
}

inline void Shape_Destroy(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Destroy(pShape);
}

typedef struct Box
{
struct ShapeVtbl* pShape;
int w, h;
} Box;


Box* Box_Create();
void Box_Destroy(Box* pBox);
void Box_Draw(Box* pBox);

Box* Box_Create()
{
Box* pBox = malloc(sizeof * pBox);
if (pBox)
{
pBox->h = 1;
pBox->w = 2;
static struct ShapeVtbl vtable = { (void (*)(void*)) Box_Draw ,
(void(*)(void*)) Box_Destroy };
pBox->pShape = &vtable;
}
return pBox;
}

inline Shape* Box_As_Shape(Box* pBox)
{
return pBox;
}

void Box_Draw(Box* pBox)
{
}
void Box_Destroy(Box* pBox)
{
}

int main(void)
{
Box* pBox = Box_Create();
Shape *pShape = Box_As_Shape(pBox);
Shape_Draw(pShape);
return 0;
}
Ben Bacarisse
2017-07-25 14:01:30 UTC
Permalink
Thiago Adams <***@gmail.com> writes:
<snip>
Post by Thiago Adams
My intention is to cast
void Box_Draw(Box*)
to
(void (*)(void*))
Maybe using

typedef void Function(void *);

would simplify the code. (I prefer not to include the pointer here.)
Post by Thiago Adams
Fix number 2 for sample 1
typedef struct ShapeVtbl
{
void(*Draw)(void*);
void(*Destroy)(void*);
} *Shape;
So this becomes:

typedef struct ShapeVtbl
{
Function *Draw;
Function *Destroy;
} *Shape;

<snip>
--
Ben.
Thiago Adams
2017-07-25 14:25:52 UTC
Permalink
Post by Ben Bacarisse
<snip>
Post by Thiago Adams
My intention is to cast
void Box_Draw(Box*)
to
(void (*)(void*))
Maybe using
typedef void Function(void *);
would simplify the code. (I prefer not to include the pointer here.)
I am trying to keep everything inside Box_Create to make the implementation suitable for C' (C prime) current mechanisms.

typedef struct Box Implements(Shape)
{
int w, h;
} Box;

//This could be generated
Box* Box_Create() default;

The code by hand in this case is not so hard to do.
If you need to add a new function on the interface Shape, let's say Rotate, then you need to create Box_Rotate, Circle_Rotate etc..
Adding the extra entry on vtable initialization will be just a
fraction of the total job.
If you remove the Rotate from the interface then you need to remove from vtable manually as well, but in this case probably you will have to
make a decision if you keep Box_Rotate , Circle_Rotate etc and this
edition will make clear that this is the time to decide.

C' implements the sample 2 automatically and it can be harder to make
and maintain my hand.
For instance:

Shape can be: Box | Circle | AdvancedShape | ShapeWithEvents;

and AdvancedShape can be

Box | Circle | Polygon;

and ShapeWithEvents can be

BoxButton| CircleButton;

Then I need to find the set of union types -
Shape can be:
Box | Circle | Polygon | BoxButton| CircleButton;
and this is used on switches
Thiago Adams
2017-07-25 14:49:54 UTC
Permalink
Post by Thiago Adams
Post by bartc
Post by Thiago Adams
Post by Thiago Adams
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
Instead of explaining what I would do differently, I did two samples.
[...]
typedef struct ShapeVtbl
{
void(*Draw)(void* pShape);
void(*Destroy)(void* pShape);
} *Shape;
inline void Shape_Draw(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Draw(pShape);
}
Box* Box_Create()
{
...
static struct ShapeVtbl vtable = { (void*)(void*) Box_Draw ,
(void*)(void*) ?
You'll have to explain that one.
My intention is to cast
void Box_Draw(Box*)
to
(void (*)(void*))
hum..I see.
Fix number 2 for sample 1
typedef struct ShapeVtbl
{
void(*Draw)(void*);
void(*Destroy)(void*);
} *Shape;
inline void Shape_Draw(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Draw(pShape);
}
inline void Shape_Destroy(Shape* pShape) {
((struct ShapeVtbl*)(*pShape))->Destroy(pShape);
}
typedef struct Box
{
struct ShapeVtbl* pShape;
int w, h;
} Box;
Box* Box_Create();
void Box_Destroy(Box* pBox);
void Box_Draw(Box* pBox);
Box* Box_Create()
{
Box* pBox = malloc(sizeof * pBox);
if (pBox)
{
pBox->h = 1;
pBox->w = 2;
static struct ShapeVtbl vtable = { (void (*)(void*)) Box_Draw ,
(void(*)(void*)) Box_Destroy };
pBox->pShape = &vtable;
}
return pBox;
}
inline Shape* Box_As_Shape(Box* pBox)
{
return pBox;
}
void Box_Draw(Box* pBox)
{
}
void Box_Destroy(Box* pBox)
{
}
int main(void)
{
Box* pBox = Box_Create();
Shape *pShape = Box_As_Shape(pBox);
Shape_Draw(pShape);
return 0;
}
Cast from Shape to Box in sample two could be like this:

static struct ShapeVtbl BoxVtable =
{ (void(*)(void*)) Box_Draw ,
(void(*)(void*)) Box_Destroy
};

Box* Shape_As_Box(Shape* p)
{
return *p == &BoxVtable ? (Box*)p : NULL;
}

The function cast like Shape_As_Box is implemented together with Box.
Shape still don't need to know who is going to implement it.

In COM, there is a virtual function QueryInterface on the vtable and each interface has an unique identifier.
Each object is responsible to apply casts. The way how COM is implement make it more dynamic. We can create class hierarchies in runtime for instance.
Malcolm McLean
2017-07-25 15:04:43 UTC
Permalink
Post by Thiago Adams
static struct ShapeVtbl BoxVtable =
{ (void(*)(void*)) Box_Draw ,
(void(*)(void*)) Box_Destroy
};
Box* Shape_As_Box(Shape* p)
{
return *p == &BoxVtable ? (Box*)p : NULL;
}
The function cast like Shape_As_Box is implemented together with Box.
Shape still don't need to know who is going to implement it.
In COM, there is a virtual function QueryInterface on the vtable and each interface has an
unique identifier.
Each object is responsible to apply casts. The way how COM is implement make it more dynamic.
We can create class hierarchies in runtime for instance.
Exactly.

Instead of a multitude of "ShapeAsBox()" type functions, you have a single "Basic Object" with
a QueryInterface method. If the "Shape" supports the "Box" interface, it then returns it.

You can work out precise rules if you want to allow multiple inheritance, or interfaces
with sub-interfaces. The general idea is straightforwards enough, however.
Scott Lurndal
2017-07-25 13:41:49 UTC
Permalink
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
FWIW, 'vtable' equivalent implementations in C date
back to the FSS (File System Switch - SVR3) and VFS (Virtual File System - SunOS/SVR4)
implementations in Unix, over thirty years ago.

It's not overly difficult.
Chris M. Thomasson
2017-07-25 18:46:40 UTC
Permalink
Post by Scott Lurndal
Post by Chris M. Thomasson
Fwiw, here is a crude "pre-alpha" attempt at getting vtables, and
allowing for only a single pointer's worth of overhead wrt using its
FWIW, 'vtable' equivalent implementations in C date
back to the FSS (File System Switch - SVR3) and VFS (Virtual File System - SunOS/SVR4)
implementations in Unix, over thirty years ago.
It's not overly difficult.
Right. Sorry for my messy, ugly code of a crap though. Well, heck, at
least it compiles! ;^)

Ummm... Here is some more of the abomination wrt adding a special
function pointer that allows a person "object" to talk. The employee
object is unchanged. Adding a function to a person does not require a
code change in an employee.

https://pastebin.com/raw/tX47puhF


the output is:
__________________
(000000000060FE20:ct_person_create):(name:Chris M. Thomasson)
(000000000060FE20:ct_person_talk):(name:Chris M. Thomasson says Hello
World!)
(000000000060FE20:ct_employee_print):(id:103)
(000000000060FE20:ct_person_print):(name:Chris M. Thomasson)
(000000000060FE20:ct_employee_destroy):(id:103)
(000000000060FE20:ct_person_destroy):(name:Chris M. Thomasson)

_______________________________________
(000000000060FE20:ct_person_create):(name:Jane Doe)
(000000000060FE20:ct_employee_print):(id:1203)
(000000000060FE20:ct_person_print):(name:Jane Doe)
(000000000060FE20:ct_person_talk):(name:Jane Doe says Goodbye)
(000000000060FE20:ct_employee_destroy):(id:1203)
(000000000060FE20:ct_person_destroy):(name:Jane Doe)
__________________


Wrt the code, think of this, a scene of a Seinfeld episode:



That's an ugly baby? :^)

Loading...