Best language to be "versatile" in game making.

Started by
67 comments, last by kop0113 11 years, 7 months ago
Karsten_, you do know that C# allows you to override the finalizer, don't you? It still involves more code than the C++ version, but you can move that try/catch block out to somewhere more appropriate. I just wrote up a simple test program in C# to try this out for myself and see if the finalizer would get called if an exception was thrown in a constructor:

[source lang="csharp"]
class Something : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed something");
}
}
class Danger : IDisposable
{
public Danger(Something something)
{
throw new InvalidOperationException();
}
~Danger()
{
Dispose();
}

public void Dispose()
{
Console.WriteLine("Disposed danger");
}
}
// Karsten_'s example class
class Test : IDisposable
{
Something something;
public Test()
{
something = new Something();
using (Danger danger = new Danger(something)) { }
}
// finalizer overload
~Test()
{
Dispose();
}
public void Dispose()
{
if (something != null)
{
something.Dispose();
something = null;
Console.WriteLine("Disposed test");
Console.ReadLine();
}
}
}
class Program
{
static void Main(string[] args)
{
Test test = null;
try
{
test = new Test();
}
catch (Exception e)
{
}
}
}

[/source]

The output I get is this:

Disposed danger
Disposed something
Disposed test


It's still a bit verbose compared to the C++ version, but arguably less so than the one with the try/catch inside Test's constructor. Granted that now the disposing will happen when the GC runs, which may not always be desirable, but you never specified that the disposing must happen immediately after the exception is thrown.
Advertisement
The finalizer does not get called if there is an exception in the constructor. The only reason why it appears to be working is that the finalizer is being run for the other classes because the program is terminating. In a game, this is unlikely to be the case unless you run GC.Collect() every loop (though even this wont help finalize some things like out of scope threads).

For example, move or copy your Console.ReadLine() to the end of Main() after letting the Test reference go out of scope and you will see exactly what I mean. Better still, throw an exception just before the end of your program (just after the new ReadLine() position) and (in mono on x86 linux) the other dispose statements will never even be called. (This is an example of not exception safe).


static void Main(string[] args)
{
Test test = null;
try
{
test = new Test();
}
catch (Exception e)
{
}

throw new Exception();
}


New output

Disposed danger
*App terminated due to thrown exception*


The only reason why danger is disposed is because it is in a "using" but unfortunately using only works within a block so can't help ensure "something" is cleaned up.
The critical issue is here though.


something = new Something();
using (Danger danger = new Danger(something)) { }


Now that "something" is constructed and we know that the Danger() throws an exception, there is nothing set up to call Dispose() on the something instance now. And if this contains a thread or another limited resource, then it is game over! Again, this is not exception safe.

So whilst the C++ code is much less verbose, it also ensures that the memory is *always* cleaned up whatever you do with it.
In C++, the destructor does not get called if there is an exception in it's constructor... However I have gotten round this in my previous solution by wrapping the something instance in a smart pointer which *does* call delete on it's contained data when it pops off the stack.

Edit: I have marked you up for your code example, since it certainly helped what I was trying to describe.
http://tinyurl.com/shewonyay - Thanks so much for those who voted on my GF's Competition Cosplay Entry for Cosplayzine. She won! I owe you all beers :)

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.

So whilst the C++ code is much less verbose, it also ensures that the memory is *always* cleaned up whatever you do with it.

You are hardly the first person to notice that Garbage Collection and RAII don't play terribly well together (it has however taken me this long to figure out that was what you were talking about).

And the long and short of it is: who cares? You only need to worry about manually finalising mission-critical resources, which a beginner doesn't need to worry about. And if typing a few extra lines in the critical case gives you the heeby-jeebies, well, one can always stick to x86 assembly sad.png

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Well then... as a fellow RAII buff, surely you feel the same frustration that a solved and very satisfactory solution to memory management is effectively ignored by the current generation of new developers just because they happen to be using a language which has effectively regressed in this area.

So I guess, I rest my case in the knowledge that the C# language is flawed but "who cares"... C++ just ain't cool.
C and C++ are both a step up from assembly... What the hell is C# a step up from?

So in my opinion, the suggested ease of C# does not outweigh it's design flaw and so I will never recommend it to people in threads such as these :)
http://tinyurl.com/shewonyay - Thanks so much for those who voted on my GF's Competition Cosplay Entry for Cosplayzine. She won! I owe you all beers :)

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.

The only reason why it appears to be working is that the finalizer is being run for the other classes because the program is terminating. In a game, this is unlikely to be the case unless you run GC.Collect() every loop (though even this wont help finalize some things like out of scope threads).


But it still runs when the GC runs. I already acknowledged that it won't be run immediately, but if you can guarantee that the GC will run on that object at some point, the finalizer will run and the object will be disposed, and there won't be a leak. Naturally, this will prove a problem if we want to actually recover from the exception, as the now potentially broken object will hang around until the next GC run. But it was never specified that the example given required whatever exception Danger throws to be recovered from, so I assumed that like almost all cases I've encountered, it wasn't, and the program terminating upon hitting an exception was desirable behaviour, and therefore I could take advantage of it. I personally prefer that my games crash on an exception even in C++, if only because their existence becomes much more apparent, usually causing me to notice them and fix whatever the problem is more quickly.

Understand, I'm not arguing in the slightest with the idea that C# is worse off for lack of full RAII, just that its "pseudo-RAII" is not as bad a situation as you're making it out to be given that a few caveats are observed and a few hoops jumped through. It's a pity stack types can't have destructors in C# - that would bring C#'s "pseudo-RAII" closer to actual RAII.

Better still, throw an exception just before the end of your program (just after the new ReadLine() position) and (in mono on x86 linux) the other dispose statements will never even be called. (This is an example of not exception safe).[/quote]

This is quite true, and is the purpose of the try/catch in the Main() function of this test program. That try/catch block ensures that the program does not crash before the garbage collector runs for the final time. Again, if that is not desirable or possible, this all falls down at our feet, as I once again acknowledge. I've not seen many cases in C# where it is not desirable, however.


The only reason why danger is disposed is because it is in a "using" but unfortunately using only works within a block so can't help ensure "something" is cleaned up.[/quote]

Interestingly, I've found that danger is NOT disposed by the using itself. If I remove the destructor/finalizer from the Danger class, I get this:


Disposed something
Disposed test


So clearly, that using never calls Dispose, because otherwise removing the Dispose() call in the finalizer would not change the output. I guess using blocks don't call Dispose() if an exception occurs in the constructor of whatever's being managed by the using block, which I must admit is a bit disappointing.

So in my opinion, the suggested ease of C# does not outweigh it's design flaw and so I will never recommend it to people in threads such as these[/quote]

So you discourage beginners from using managed languages in general, I take it?

I wouldn't call it a "design flaw," personally. As has been mentioned, RAII and garbage collection don't really play nice together. If you're using a language with garbage collection, chances are very good that you don't actually care very much about exactly when your resources get released, even in exceptional conditions - you just care that they ARE released, at some point. In fact that's... kind of the whole point of garbage collection.

So I guess, I rest my case in the knowledge that the C# language is flawed but "who cares"... C++ just ain't cool.


And you are saying C++ doesn't have flaws in it too? That some how it is the 'perfect' language?
Get real.

C++ has more flaws in it than C#, more flaws which will trip up someone who isn't using it AND has less productivity associated with it in situations where that matters more.

And before you bash me as some C# fanboy I should point out that my primary coding enviroment is C++ and will likely continue to be C++ for some time however to dismiss C# over some trivial issue (and it really is) and thus avoiding the benefits of the language and the massive class library which comes with it is frankly idiotic.

It's not a matter of 'what is cool' its a matter of 'what is the right tool for the job'.
Sometimes that tool is C++.
Sometimes that tool is C#.
Sometimes that tool is Python.
Or some other language or method.

If you want to wander around with a set of 'C++ is bestest!' blinkers on then feel free; be prepared to be taken to task over it however because most of us who work for a living with the language know better... apprently much better.. and apprently have a much better appreciation for other languages and tool sets.

I guess using blocks don't call Dispose() if an exception occurs in the constructor


Yeah, thats where my hacky try / catch Dispose(); throw in it's constructor solution came from in my C# example. It isn't ideal but it seems a safe compromise. :/


So you discourage beginners from using managed languages in general, I take it?


Well, for games, yes. I simply don't feel that garbage collected languages are the correct tool for the job. For a business application or server then yeah, I don't see a problem with managed languages.

I find it quite wrong that so many new games developers on this forum are steered away from C++ when it is infact the perfect language for this task and is much more suited to games and other high performance software than any other language currently in use today.


C++ has more flaws in it than C#


Oh? Such as? Until you can give me an actual example(s), this statement is useless. As for thinking correct memory management is a "trivial issue", I think that is an extremely careless oversight on your behalf.


the massive class library which comes with it


Class library? What the heck has that to do with anything related to a language? I can easily utilize that from C++/CLI or if I really wanted a 100% native solution, I could use Qt. Certainly not going to cripple my code design for a few "nifty" classes. (many of which arn't even needed in game dev anyway).
http://tinyurl.com/shewonyay - Thanks so much for those who voted on my GF's Competition Cosplay Entry for Cosplayzine. She won! I owe you all beers :)

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.

[quote name='phantom' timestamp='1347405751' post='4979108']
C++ has more flaws in it than C#


Oh? Such as?[/quote]

How about the ones described in http://yosefk.com/c++fqa/?

Class library? What the heck has that to do with anything related to a language?[/quote]

A lot, actually. If a class library is so inseparable from a programming language to the point where it's described in the actual language spec (as is this case for the C# and C++ standard libraries, I believe), then for all intents and purposes, that library is a part of that language. Even if a class library is not sufficiently tied to the language to qualify for being part of the language, if it is widely-enough available it will grow into the common idiom of that language so much as to be inseparable enough that knowing and using it will be inescapable.

Languages (both natural and artificial) are more than syntax. They are also idiom and culture. Class libraries in general contribute to the latter two.
Perhaps read this one...
http://yosefk.com/c ....html#defect-10

<rant>This could be a good idea in some cases if C++ exceptions were any good. They aren't, and can't be - as usual, because of another C++ "feature"... snip</rant>

This guy sounds like a moron.

And this one...
http://yosefk.com/c ....html#defect-13
He goes into detail to explain the issues with manual memory management.. then mentions smart pointers and trails off. He didnt actually care to elaborate on C++'s elegant and unique solution to manual memeory management?

As for a class library being part of a language, when .NET offers the same classes on any of their compatible languages, it certainly isnt a sane argument for choosing C#.

I guess we can resume this discussion in a few years when C# is long forgotten and C++ is still uncool. smile.png
http://tinyurl.com/shewonyay - Thanks so much for those who voted on my GF's Competition Cosplay Entry for Cosplayzine. She won! I owe you all beers :)

Mutiny - Open-source C++ Unity re-implementation.
Defile of Eden 2 - FreeBSD and OpenBSD binaries of our latest game.

This topic is closed to new replies.

Advertisement