A programming bedtime story, and a pseudo-postmortem

Member
Posts: 45
Joined: 2006.11
Post: #1
(warning: parts of this story are over-dramatized Rasp )
I used to be a die hard C++ advocate, even after trying several other languages (Java, Objective C, C#, LISP, Ruby), but I still always fell back to C++ as my language of choice.

That is, until I started The Green Engine. This project, part of my senior design course at my university was (and is) the largest project I've created from scratch. And I quickly found that C++ was getting in the way.

The project contains a very simple custom scripting language and interpreter (designed for very inexperienced programmers). Internally, the engine uses various associative data structures which sometimes share references to the same objects. Knowing this would happen from the beginning I chose to use the boost library's shared_ptr template to handle reference counting unobtrusively for all shared objects in the engine. And that's where the trouble began.

You see, shared_ptr is a class template that is supposed to work much like a pointer, but it also keeps track of a reference count of the number of shared_ptr that are pointing to the same object, and automatically deletes an object when the last shared_ptr referencing it releases it (by going out of scope or being reassigned to another reference, or deleted itself).

Now, in the green engine, there are entities, behaviors, and messages, and all of them share some functionality. They all have a table of named variables (which are assigned integer keys for fast access), they all have "constructor" functionality in the scripting language (they can be instantiated with a parameter list, which runs a user-defined script on those parameters).

So, naturally, I wrapped all of this shared functionality into a virtual base class, from which the entity, behavior, and message objects derive. In addition, the "template" for a user-defined message/entity/behavior, and an "instance" of that message/entity/behavior are separated into separate classes. So far so good.

Now, there needed to be a place to store all the templates and instances in the user-defined game. So there are template tables and instance pools. Of course, there is a virtual base class for both template tables, and instance pools.

Here's where the problems start. The base classes for template tables and instance pools have to define methods that the subclasses override with some added functionality. Some of these methods have to return a reference (through a shared_ptr, naturally) to a template or instance. Now, since all templates and instances inherit from the same base class, the logical thing to do was return a reference to that base class type.

But shared_ptr is a template class, NOT a naked pointer. If you assign a shared_ptr<EntityTemplate> to a shared_ptr<Template> return value, it will either not compile at all, or worse, give a false reference count. Boost's solution was to require calling a template function to dynamically cast the shared_ptr<Template> into a shared_ptr<EntityTemplate>. Fine, except it looks like this:
Code:
boost::dynamic_pointer_cast<EntityTemplate, Template>(shared_ptr<Template> x)
which quickly makes code look messy.

Since the cast does look messy, I made it a point to write a small macro for it for each class, so instead of the above, I just have ENTITY_CAST(x) to do the dynamic_pointer_cast to turn x into shared_ptr<Entity>.

That's a little better, but now every time I want an entity template, I still have to do an ENTITY_CAST. So, instead I override all methods in the template and instance table subclasses that return a shared_ptr or take one as an argument, and make the return values shared_ptr<subclass>, and return the result of the base class's method, dynamically cast to a shared_ptr<subclass>.

Alright, so that works, but now I have to override pretty much every method of the base table classes just to dynamic cast the arguments or the return values. Even if I don't change anything.

So yeah, all of that effort spent just to get reference counting working in C++ that plays nice with templates and inheritance used at the same time. If I had used Java, I could have simply used the built in garbage collector and implemented my little shallow inheritance trees with no problem at all.

So, in short, even a die hard c++ user can, given the right motivation, change his mind about his favorite language.

The End,
JeroMiya
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #2
Funny, it seems to be scripting languages that are a very rough on C++. I have similarly gotten pretty fed up with C++ while writing a scripting language, but for different reasons.

Unlike you, I knew from the start to stay clear of boost Smile

I opted for a simple tricolor garbage collector instead. Its working nicely, as far as I can tell, though probably a little less performant than your approach, but it is simpler to code for.

Either way, the language was basically an Io clone, which is a message passing / prototyping based language. This in essence results in lots of temporary objects being created and destroyed, and while a rather large part of the run time (approx 20%) is expected to be spent on GC, I was surprised to see more like 90% time spent there, mostly due to C++'s idiotic handling of virtual destructors. This is pretty much a deal breaker on its own, if I hadn't run into issues similar to yours with type casting, as well.

So, the end result of this little adventure is that I am currently doodling with writing a compiled language to hopefully replace C++ in my future projects Smile (needless to say, nobody thinks I can do it. we'll see what they say when my worldwide emp... we'll see Wink )
Quote this message in a reply
Moderator
Posts: 373
Joined: 2006.08
Post: #3
[offtopic]
Good luck with the compiled language, man..I myself was thinking of writing a sort of 'C++ parser' that took C++ code, turned it into efficient C code, and then compiled the C code, thereby saving me the time of writing an actual compiler.
Too complex for me, but if you ever get the compiled language done, I definitely want to see it Smile
[/offtopic]
-wyrmmage

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Moderator
Posts: 439
Joined: 2002.09
Post: #4
wyrmmage Wrote:Good luck with the compiled language, man..I myself was thinking of writing a sort of 'C++ parser' that took C++ code, turned it into efficient C code, and then compiled the C code, thereby saving me the time of writing an actual compiler.

Not sure what you would gain. C++ compiled code is just as efficient as C compiled code, except if you use certain C++ features. And there is no way around that: if you declare a virtual function then there has to be code that resolves the function call at runtime, which will be oh-so-very-slightly slower than calling a "regular" function.

Conversely, you are free to only use the fast C++ features (roughly equivalent to programming in C) and the compiler will generate more or less the same code as a C compiler.

Also, what you describe used to exist- it's how C++ was implemented on most platforms in the early days.
http://en.wikipedia.org/wiki/Cfront

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Member
Posts: 45
Joined: 2006.11
Post: #5
It's not going to be a compiled language, just an interpreted language.

And it's probably not a good idea to start a C++ vs. C conversation. They tend to not accomplish anything.
Quote this message in a reply
Moderator
Posts: 373
Joined: 2006.08
Post: #6
Thanks for the interesting article, MattDiamond; My project would have been more of a proof-of-concept, but seems there's not much point if that's how it used to be done anyway Rasp
-wyrmmage

Worlds at War (Current Project) - http://www.awkward-games.com/forum/
Quote this message in a reply
Post Reply