I understand the want for simplicity in C (and Go gets closer, but has its issues), however, it seems in the end it drags you down.
Just having the C++ ability to have objects doing things is very helpful. But yeah, C++ has the ability to get very complicated
But it's your choice to have "complicated C++". Limit yourself to some functionalities and it's much more manageable.
Use basic STL and keep it simple (also C++11 at least) and it's a very pleasant experience (or less worse experience, depending on your point of view).
There are some arguments to stick with C instead of a very C-like subset of C++. Off the top of my head:
- Recompiling and reloading parts of your game at run-time is quite easy in C. In C++ you have to make sure (at least) that nobody has pointers to vtables of the dll at the time of reload. This can be a bit tricky if you're using things like std::function in your dll code. Yes, you could be using a scripting language, but thinking how to match the semantics of a scripting language with your engine, where to draw the line, and then write the glue code is a lot more work than just reloading some plain C functions. And if you later decide this was a bad/worthless idea, reverting from dynamic C code to static is almost a no-op, whereas reverting back from scripts is dreadful.
- A quick & dirty reflection is easy when you don't have to deal with name mangling, templates, and overloading. Just some script scanning through your code and outputting elementary type info to .c file may be enough for things like real-time memory browser-editor for your whole engine. This can be very valuable when developing new engine features, as you can view and edit, and maybe draw even graphs of members you just added to some struct. Also useful for modifying game object data on the fly when debugging/creating levels.
To me it looks like it you would like to use plain C as scripting language for game logic code. Your arguments make sense there and the trade of looks reasonable.
However, it doesn't make necessarily sense put the restrictions across the complete source code of game, just because the game logic benefits from it.
Maybe, I don't know. This is just me trying to minimize the unnecessary pain and suffering while waiting for a better language to arrive. In that context arguing which bad solution is less worse seems somewhat pointless, and also depends on personal taste. I'll have to see this project through to know better.
"real-time memory browser-editor for your whole engine."
Can you describe very briefly how you have the server set up? I've wanted to add this to my c hobby projects for a long time and would enjoy any tidbits of the practicalities involved.
Very interesting approach, and pretty straight-forward as you said. Thanks for sharing that.
Have you considered letting the compiler produce DWARF-formatted debug information and using an existing DWARF library to handle the symbol to address mapping? I've had good success with this method for controlling embedded systems from a desktop PC, though not when the host and target are the same computer.
I started thinking about it, but quite fast decided to roll my own self-contained system. Not because it was an informed decision, but felt more fun :P
Check out the first few episodes of Casey Muratori's Handmade Hero series where he implements an extremely simple hot code reloading system in C (actually C++, but he doesn't use almost any C++ features, certainly not vtables).
if you don't want to watch the video, here is a basic overview tldw; of the technique:
on every run of the game loop(usually every frame), reload a dynamically linked module (dll for windows, .so for linux), which contains the actual code you want to run every frame. The function you then invoke from the module must be passed the entire block of memory allocated for the game state. You then just recompile the dll/so module when you make a change, and the game would execute the new code on the next frame. Adding new data structures is OK as long as you don't mangle an existing data structure...but because a game can expect to work with a constant block of pre-allocated memory, this actually works fine most of the time...
yes - but different OS have different ways to load dynamically linked modules, and the casey video only showed the windows method. But it's basically the same, just library calls differently named.
It's quite restricted still. For example changing datatypes during recompilation is problematic, at least if you don't destroy the instances before reloading, and re-instantiate afterwards. I don't bother to do that, because I see most of the value in things like tweaking game object logic repeatedly, which fits the restrictions nicely.
C++ when it is practiced in a "standard" way using the standard library and all the standard advices (such as using boost libraries whenever possible) employs a style of programming that generates a _lot_ of intermediary, supposedly zero-cost abstractions that the optimiser then have to work hard to remove.
This is why I say supposedly zero-cost because although your users may not pay for them, you as a developer will pay for them through either slow compilations or alternatively slow debug builds.
C++ can be a good tool if you have the right discipline, however the discipline is hard to follow if you bring a lot of dependencies in.
C-structs work fine. In my experience, for things like games, C-structs are capable of doing everything you need objects to do. The lack of polymorphism and other OO stuff, makes C code easier to reason about and maintain.
The original creator of the C++ STL has said in interviews that after a long career of C++ development, he still never uses -- and sees no use for -- any model of inheritance for anything. I've since learned that my own C++ code is much better when I very sparingly, or never, subclass anything. This has the added benefit of also never using vtables.
I think that's what most engine developers do nowadays. Sticking to some part of C++ to a point that it looks like C with classes. Hey, some don't even use STL and go with their own libraries.
>>Just having the C++ ability to have objects doing things is very helpful.
There is a school of thought that it's the opposite of helpful. They would say it's better to have functions doing stuff with data (either taking it as input and returning as output or modifying some structures which should be mutable). "Objects doing things" don't fit to that model.
I can't agree with this, though I do have respect for Qt. But, having to put magic macros inside all your subclasses doesn't really make things simpler... and, Qt's own libraries for doing many of the things that the STL does are mostly of the same complexity as the analogous tools in the STL.
The "magic macros" aren't a big deal; you just stick "QOBJECT" at the top of every class, it's not hard, and it's just one line (and one word). And the complexity of Qt's libraries is irrelevant; the whole point of using a toolkit like Qt is so that it hides the complexity for you and gives you tools in an easier-to-use format instead of you reinventing the wheel over and over, or getting different tools from different places, of varying quality. In the process of that, Qt (much like Boost) attempts to provide all the tools you'll need for general tasks, rather than just supplementing STL by providing lots of stuff it lacks.
Also, STL syntax is ugly and hard to work with; Qt's is a lot easier, which is another reason they replace STL's functionality with their own. (Boost, similarly, has its own style of syntax.)
(If you're referring to any macros related to the signal/slot mechanism, that's because C++ itself has no such mechanism natively so of course it's going to require extra macros and a preprocessor to provide that.)
In my opinion, with which many will probably disagree, there's nothing wrong with using STL containers in moderation if you approach it right. std::vector is good enough and fast enough in most cases if you reserve space and are judicious with allocations.
Maybe, but personally I think STL syntax is horribly ugly and not easy to read or work with, whereas Qt (at least to me) is much easier to parse, in addition to offering a lot more functionality and flexibility. (Qt has a lot more container types, for instance.)
Just having the C++ ability to have objects doing things is very helpful. But yeah, C++ has the ability to get very complicated
But it's your choice to have "complicated C++". Limit yourself to some functionalities and it's much more manageable.
Use basic STL and keep it simple (also C++11 at least) and it's a very pleasant experience (or less worse experience, depending on your point of view).