Classes in C
sort of…
The Problem
I have been making my game engine in C++ because I expected C++ to be C with classes. C++ is a different language from C entirely.
It has a different standard library and a different syntax.
YES A DIFFERENT SYNTAX.
- Structs are typedef by default; and they’re not really structs either, they’re classes. It sounds like a feature but then when it comes to integration with other projects, they’ll have all sorts of bullshit datatypes all over the place. Yes datatypes, not data blocks. Because in C++, structs are classes. And so are enums.
- Template Metaprogramming. I am sorry but what the fuck. My code looks like HTML and C had an incest baby. Compared to C, it just feels like I am fighting with my source code trying to get it to work; with C, if there was an issue, it always felt like it was my fault (which is a good things); with C++, it feels like it’s the language’s fault.
- RAII. In all honesty, I liked RAII initially. If I allocate memory in a constructor, I deallocate in the destructor, keeps me from worrying about it too much. But the more I worked with memory, the less I use RAII and the more I found out C++ really doesn’t want you handling memory.
The Bigger Problem
Yes, I am addicted now. I have to put in extra effort getting the Venom suit off.
I, too, found the C++ “features” to be features in the very begining.
std::cout << gugu << gaga;instead of
printf("%c%c", gugu, gaga);wherein I get an error in the second one becase it should be %s%s instead of %c%c because characters of the alternate character set are more than one byte.
Knowing this, I made my game engine OO based.
levelObject
class levelObject {
public:
virtual void Begin() {}
virtual void Tick() {}
};And then a player can be.
class player : public levelObject {
public:// Input Callbcak functions like Move, Jump etc. void Begin() override {
}
void Tick() override {
}
};
Basically objects in a level can be classes with custom behaviour, and the only requirement is that they inherit from leveObject.
class enemy : public levelObject {
public:
void Begin() override {
}
void Tick() override {
}
};And moreover I can have custom classes.
class staticObject : public levelObject { }
class floor : public staticObject {};
class pillar : public staticObject {};level
And finally a level can be
class level {
public:
std::vector<std::unique_ptr<levelObjects>> objects;
void Begin() override {
objects.push_back(std::make_unique<player>());
objects.push_back(std::make_unique<floor>());
objects.push_back(std::make_unique<pillar>());
objects.push_back(std::make_unique<pillar>());
objects.push_back(std::make_unique<pillar>());
objects.push_back(std::make_unique<pillar>());for (const auto& obj : objects) {
obj.Begin();
}
} void Tick() {
for (const auto& obj : objects) {
obj.Tick();
}
}
};
mainloop
int main() {
level firstLevel;
level.Begin();
while(AppOpen) {
firstLevel.Tick();
}
}Very convinient. But, the language was now. I still do not know the difference between new and std::make_unique.
And given my preferred coding style; I really don't like the challenges this will bring me down the road.
The Solution
Implement the methods as functions.
typedef struct {
void* Begin();
void* Tick();
} levelbject;player.c
void player_begin() {
}
void player_Tick() {
}typedef struct {
levelObject base;
} player;player *spawn_player() {
player *p = calloc(1, sizeof(player));
p->base.Begin = player_begin;
p->base.Tick = player_begin;
}
level.c
typedef struct {
LevelObject **objects;
size_t count, capacity;
} Level;void level_add(Level *l, LevelObject *obj) {
if (l->count == l->capacity) {
l->capacity = l->capacity ? l->capacity * 2 : 16;
l->objects = realloc(l->objects, l->capacity * sizeof(LevelObject *));
}
l->objects[l->count++] = obj;
}void level_tick(Level *l) {
for (size_t i = 0; i < l->count; i++) {
LevelObject *o = l->objects[i];
o->tick(o, dt);
}
}
and then in the mainloop
Level level;
level_add(&level, (LevelObject *)spawn_player(0, 0));
level_add(&level, (LevelObject *)spawn_player(0, -10, 100));while (AppOpen) {
level_tick(&level);
}This might seem more convoluted than the Actor-based approach but imagine debugging that compared to this.
This was just an example. Yes there has to be a custom implementation of dynamic arrays without the feature that comes with C++, for destroying levelObjects and wahtnot. And the lack of a key,value pair makes the inputHandling much harder as well. But at the end of the day,
Get Swaroop Nayak’s stories in your inbox
Join Medium for free to get updates from this writer.
I don’t need to learn anything new to understand the code.
I don’t feel like I am fighting with the language.





