K
Kamilche
I have looked at many object-oriented programming frameworks out there
for C. Though the ideas presented are intriguing, and I've used some
of them in my own work, they all suffered some drawback or another.
I have created a new method of doing OOP with C, something that fits
my needs particularly well. I haven't encountered anything else like
it, so I'm tossing it out to the Internet for comment.
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=6820&lngWId=3
Here's a bit more info about it, in case you're interested:
This framework allows for object oriented processing in C.
Instead of structs with hard-coded fields, every object is a
hash table that can contain an infinite number of name/value pairs,
called 'stats' from here on out. So - instead of this:
struct PERSON_T
{
char *name; // filled with 'Harry
int age; // filled with '17'
}
you'd have this instead:
HASHTABLE (list of name/value pairs)
StatName StatValue
-------------- --------------
name Harry
age 17
(The stat names are actually stored as enums, for maximum lookup
speed.)
Each type of object that can exist, such a 'square' or 'circle,'
must have an entry in the master table, that sets the 'default field
values' for that type of object. This entry is called a 'master item.'
Each master item in the master table is itself a hash table, so the
master table can be considered a 'table of tables.'
When accessing the fields of an object, use GetStat or SetStat.
They work in the following manner:
* GetStat - Retrieves the value from the object, if that
name/value pair exists.
If it doesn't, retrieves the value from the
master table for that object type.
* SetStat - Sets the value in the object only -
never modifies the master table!
This is an 'auto-optimizing' memory scheme. Only the differences
between the object, and the 'master object' for that type, are stored.
This allows you to create millions of items in memory, with thousands
of stats each, in a very small amount of space.
If the stat exists in the object, it is pulled from the object.
If it doesn't, it assumes 'the default value' from the master is
good enough, and uses that instead.
Constructing objects is easy - just call the Create routine with the
name of the master object, plus all the stats you want to override.
In addition to storing the default stats, the master table stores
function pointers to event handlers for that object.
Look at the following master item example:
CIRCLE
StatName StatValue
---------- --------------
Type 200
Name CIRCLE
Inherits SHAPE
Radius 1
OnDraw circle_draw
OnSetRadius circle_setradius
This master item describes a new object type called 'CIRCLE', that
has a type of 200, a name of CIRCLE, and a default radius of 1.
When events are sent to the object, it looks for the corresponding
'OnEvent' stat for the specified event, and calls the function
through that function pointer. For instance - when the 'draw'
command is sent to it, it pulls up the 'OnDraw' stat, and picks out
the function pointer. Then, it executes the 'circle_draw' routine
through that function pointer. When a 'setradius' command is sent
to it, it executes the 'circle_setradius' routine.
It also illustrates the 'inheritance' feature. Inheritance comes into
play only when loading the master file. When an 'Inherits' stat is
encountered, the master loading routine pulls up the specified master
item, and pulls all ITS stats out and adds it to the current master
item.
In this case, the master table entry for 'SHAPE' looks like this:
SHAPE
StatName StatValue
---------- --------------
Type 100
Name SHAPE
Inherits BASE
OnMove shape_move
OnSetName shape_setname
All stats except type and name will be added to the CIRCLE master
item.
Though not illustrated, you can inherit from multiple base types.
Simply specify multiple INHERITS stats when loading the master file.
The stats will be pulled in order, with newer entries overlaying older
entries, so be careful to specify the inheritance loading order
accordingly!
After all the inheritance is performed, the CIRCLE item actually looks
like this in memory:
CIRCLE
StatName StatValue
---------- --------------
Type 200
Name CIRCLE
Radius 1
OnDraw circle_draw
OnSetRadius circle_setradius
OnMove shape_move
OnSetName shape_setname
As you can see, 'draw' and 'setradius' are specific to the circle
master item. 'move' and 'setname' are inherited from the 'shape'
master item. (The 'inherits' stat is missing entirely, as that is
only used when loading the master table into memory.)
All method calls are done via 'Do'. Do takes the item,
the event, and any additional arguments. It looks up which stat is
associated with the event, and retrieves that function pointer.
If the function pointer exists, it executes it with the supplied
arguments. If it doesn't exist, it returns an ERR_UNHANDLED_EVENT
error.
All possible events must be listed in the 'events.txt' file, and
all possible stats must be listed in the 'stats.txt' file.
All the errors are listed in the 'errors.txt' file.
A stringizing macro STRINGIZE is used to turn these into the
appropriate enum and arrays in code.
That's about it. I've stripped this down from my own code to its
bare essentials. In my code, I load the master table from disk, and
so should you! This makes it easy to modify functionality of objects
without cutting any code. I also have specialized 'load from disk' and
'save to disk' handling for the objects, not implemented here.
As you might guess, it's only necessary to store the stats from the
object hash table, not the master item hash table.
The master table should only ever be modified 'by hand'.
I hope you find some of these concepts useful in your own work!
If you do any enhancements, drop me a line.
--Kamilche, (e-mail address removed)
for C. Though the ideas presented are intriguing, and I've used some
of them in my own work, they all suffered some drawback or another.
I have created a new method of doing OOP with C, something that fits
my needs particularly well. I haven't encountered anything else like
it, so I'm tossing it out to the Internet for comment.
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=6820&lngWId=3
Here's a bit more info about it, in case you're interested:
This framework allows for object oriented processing in C.
Instead of structs with hard-coded fields, every object is a
hash table that can contain an infinite number of name/value pairs,
called 'stats' from here on out. So - instead of this:
struct PERSON_T
{
char *name; // filled with 'Harry
int age; // filled with '17'
}
you'd have this instead:
HASHTABLE (list of name/value pairs)
StatName StatValue
-------------- --------------
name Harry
age 17
(The stat names are actually stored as enums, for maximum lookup
speed.)
Each type of object that can exist, such a 'square' or 'circle,'
must have an entry in the master table, that sets the 'default field
values' for that type of object. This entry is called a 'master item.'
Each master item in the master table is itself a hash table, so the
master table can be considered a 'table of tables.'
When accessing the fields of an object, use GetStat or SetStat.
They work in the following manner:
* GetStat - Retrieves the value from the object, if that
name/value pair exists.
If it doesn't, retrieves the value from the
master table for that object type.
* SetStat - Sets the value in the object only -
never modifies the master table!
This is an 'auto-optimizing' memory scheme. Only the differences
between the object, and the 'master object' for that type, are stored.
This allows you to create millions of items in memory, with thousands
of stats each, in a very small amount of space.
If the stat exists in the object, it is pulled from the object.
If it doesn't, it assumes 'the default value' from the master is
good enough, and uses that instead.
Constructing objects is easy - just call the Create routine with the
name of the master object, plus all the stats you want to override.
In addition to storing the default stats, the master table stores
function pointers to event handlers for that object.
Look at the following master item example:
CIRCLE
StatName StatValue
---------- --------------
Type 200
Name CIRCLE
Inherits SHAPE
Radius 1
OnDraw circle_draw
OnSetRadius circle_setradius
This master item describes a new object type called 'CIRCLE', that
has a type of 200, a name of CIRCLE, and a default radius of 1.
When events are sent to the object, it looks for the corresponding
'OnEvent' stat for the specified event, and calls the function
through that function pointer. For instance - when the 'draw'
command is sent to it, it pulls up the 'OnDraw' stat, and picks out
the function pointer. Then, it executes the 'circle_draw' routine
through that function pointer. When a 'setradius' command is sent
to it, it executes the 'circle_setradius' routine.
It also illustrates the 'inheritance' feature. Inheritance comes into
play only when loading the master file. When an 'Inherits' stat is
encountered, the master loading routine pulls up the specified master
item, and pulls all ITS stats out and adds it to the current master
item.
In this case, the master table entry for 'SHAPE' looks like this:
SHAPE
StatName StatValue
---------- --------------
Type 100
Name SHAPE
Inherits BASE
OnMove shape_move
OnSetName shape_setname
All stats except type and name will be added to the CIRCLE master
item.
Though not illustrated, you can inherit from multiple base types.
Simply specify multiple INHERITS stats when loading the master file.
The stats will be pulled in order, with newer entries overlaying older
entries, so be careful to specify the inheritance loading order
accordingly!
After all the inheritance is performed, the CIRCLE item actually looks
like this in memory:
CIRCLE
StatName StatValue
---------- --------------
Type 200
Name CIRCLE
Radius 1
OnDraw circle_draw
OnSetRadius circle_setradius
OnMove shape_move
OnSetName shape_setname
As you can see, 'draw' and 'setradius' are specific to the circle
master item. 'move' and 'setname' are inherited from the 'shape'
master item. (The 'inherits' stat is missing entirely, as that is
only used when loading the master table into memory.)
All method calls are done via 'Do'. Do takes the item,
the event, and any additional arguments. It looks up which stat is
associated with the event, and retrieves that function pointer.
If the function pointer exists, it executes it with the supplied
arguments. If it doesn't exist, it returns an ERR_UNHANDLED_EVENT
error.
All possible events must be listed in the 'events.txt' file, and
all possible stats must be listed in the 'stats.txt' file.
All the errors are listed in the 'errors.txt' file.
A stringizing macro STRINGIZE is used to turn these into the
appropriate enum and arrays in code.
That's about it. I've stripped this down from my own code to its
bare essentials. In my code, I load the master table from disk, and
so should you! This makes it easy to modify functionality of objects
without cutting any code. I also have specialized 'load from disk' and
'save to disk' handling for the objects, not implemented here.
As you might guess, it's only necessary to store the stats from the
object hash table, not the master item hash table.
The master table should only ever be modified 'by hand'.
I hope you find some of these concepts useful in your own work!
If you do any enhancements, drop me a line.
--Kamilche, (e-mail address removed)