C++ and games programming: the Wesnothian Approach

Discuss development of or ideas for other Free games here, as well as other games in general.

Moderators: Forum Moderators, Developers

C++ and games programming: the Wesnothian Approach

Postby Dave » June 25th, 2005, 2:16 am

Well, a while ago I wrote an 'essay' about games programming, but despite alot of nice positive feedback, I haven't written another one. Well, I decided that since alot of people 'question' why Wesnoth is written in C++, I thought it'd be good to write an essay (hopefully not a rant!) on our experiences with Wesnoth on C++, and its advantages and disadvantages.

So, why'd I choose C++ to write Wesnoth? After all, it's a rather ugly language, and it's hideously difficult to learn.

I think the 'difficult to learn' part is pretty much a deal-breaker for anyone who doesn't already know it well before they start their project, unless part of the intent of embarking on a project is to learn C++. Personally, I already knew C++, so it wasn't a barrier.

There is another side-effect though, and that is that the pool of programmers who know C++ is alot smaller than the pool of programmers who know C, or Java, or Perl, or Python. This can be significant, especially if you're hoping to have alot of people working on your project.

Well, I wasn't really expecting anyone to help with programming on Wesnoth. I was hoping for some help with graphics certainly, but I thought I had the programming side pretty much covered -- so, this wasn't a barrier for me either. As a side note, I recommend anyone starting a project to plan on getting no help in the programming department. Substantial chunks of programming help didn't show up for a long time in Wesnoth development, and I think it's the same with most projects.

So, C++ being difficult to learn wasn't much of a barrier for me, but I do think it would be for someone who doesn't know it pretty well before they start their project. Now we have the matter of C++ being well...ugly, and often much lower-level than other languages. What advantages does it have to overcome these disadvantages?

Being an Open Source project, Wesnoth has relied heavily on people -- especially me -- being able to add a substantial chunk to the project in a small amount of time. I'm an impatient person, and I just had to be able to work for an 8, or 4, or 2, or even 1 hour chunk on Wesnoth and have something to show for it at the end. The work also had to be interesting (for me) and fun.

To start with, my development model couldn't rely heavily on unit tests. I wouldn't mind writing some unit tests for easy-to-encapsulate modules, such as the parser, but relying on unit tests for every module as a normal part of my development cycle really wouldn't work for me. It'd make development boring, and slow it down: having to write unit tests for all code I wrote would make it much more difficult to implement a feature in a one or two hour time slice.

I know lots of people swear by unit tests, and I'm not really going to go into why I don't like using them, but I will simply say that I don't, and I don't think Wesnoth would have gotten off the ground if it had to rely on unit tests. For someone who doesn't mind or likes writing unit tests, then a different development model may work better.

Anyhow, with no unit tests, I had to have a language that had lots of static checking built-in to catch common errors I would make when trying to develop fast. This quickly eliminated languages like Python and Perl. In fact, even a language like Java is more or less eliminated, since it's static checking isn't nearly as comprehensive as C or C++'s.

For instance, in C++ one can use the const keyword to promise that an object won't be modified:

Code: Select all
void display_unit(const unit& myunit);


The 'display_unit' function shouldn't modify the unit given to it, and it indeed promises this by making the unit const. The compiler will emit an error if the function attempts to modify the unit.

Understanding how const works exactly is one of the many difficult aspects of C++ to master, but once it's second-nature the time-savings are impressive with the number of errors the compiler spots for you.

C and Objective-C are the only other major languages I know of with equivalent features to this.

Unfortunately, C has the serious disadvantage of not containing generics, and that means that it's almost impossible to implement type-safe containers in it. So for instance, in C if you had a list of units, and wanted to inspect the last one, you might have to do something like this (with a typical list implementation):

Code: Select all
unit* u = (unit*)list_get_back(units);


This is nasty because the compiler is doing an unchecked conversion -- assuming that what is in the container is a unit. The compiler has no way to check for you that it's really a unit, and if you had some code that accidentally inserted some other type of object in the container, you would get a nasty crash.

In C++, you can make a list of unit objects, and the compiler will guarantee at compile-time that only genuine units go into it, and then you can retrieve a unit without a cast:

Code: Select all
unit* u = units.back();


At the time I started Wesnoth, Java had the same problem as C, though later versions of Java have implemented basic generics which support this. Python, Perl, and similiar languages have the same project as C in this regard. The languages other than C do have sufficient introspection capabilities to allow one to determine at run-time the type of the object, but this isn't even close to as useful as static type checking.

Many people don't like static type checking for various reasons. However almost all of the intelligent arguments I have seen against it advocate some kind of unit testing system instead. Unfortunately, this still places the burden on the programmer, while static type checking makes the compiler easily spot many possible errors.

There is another advantage of C++ though, which I consider to possibly be its biggest advantage over any other imperative language. It's also a hugely under-rated and misunderstood advantage. That advantage is the presence of destructors.

Operations in programming often occur in pairs. You allocate memory, then later you release memory. You open a file, then later you close it. You lock a mutex, then later you unlock it. You begin an operation, then later you either commit it or roll it back.

Unfortunately, a very common programming error is to have cases where an operation is performed, but its partner operation isn't. For instance, suppose we have a function to read a file and process it in C:

Code: Select all
int read_file(const char* fname, int fsize)
{
    char* ptr = (char*)malloc(fsize);   
    FILE* file = fopen(fname,"r");
    fsize = fread(ptr,1,fsize,file);

    /*process the file here*/

    fclose(file);
    free(ptr);

    return SUCCESS;
}


Well, there's a problem with that code. What if the file didn't open properly? So, someone spots the bug, and wants to fix it nice and fast, so they just add this below the fopen() call:

Code: Select all
if(file == NULL) {
    return ERROR;
}


But...oops...they forgot to free() the memory allocated by the malloc(). This problem is called a memory leak.

Now, of course, many languages solve this problem through garbage collection. That is, the program doesn't have to free memory allocated, the language will use some mechanism to automatically detect when it is no longer used, and reclaim it.

This is fine as far as it goes, but memory allocation and deallocation is only one of many paired operations in programming. How about opening a file and closing a file? Or opening a socket and closing a socket? Most languages with garbage collection do support operations known as 'finalizers' which are called when an object is to be destroyed, and they could close sockets and files, but they have a serious problem in that the languages make no guarantees of when they will run -- or even whether they will be run at all.

Once in a project at a company I had some code like this which I had to maintain:

Code: Select all
void graph::draw()
{
   lock_display(); //stop operations being drawn to the screen so there is no flickering

   //complex drawing operations which call lots of other functions
   ...

   unlock_display(); //update the display
}


Well, somewhere, somehow this function was occasionally exiting without unlock_display() being called. This meant that the graph was never unlocked, and would appear to the user to freeze up, permanently. It was nasty to debug, to find out when exactly this could happen.

You can't trust an operation like this to a finalizer, because the finalizer might not get called for a long time, and it might not get called at all.

Fortunately C++ has a solution to this in destructors. Destructors are guaranteed to be run when an object is destroyed, and objects created on the stack are guaranteed to be destroyed when their scope ends. So I re-wrote the above function like this:

Code: Select all
class display_locker
{
public:
    display_locker() {lock_display();}
    ~display_locker() {unlock_display();}
};
...
void graph::draw()
{
   display_locker locker;

   //complex drawing operations which call lots of other functions
   ...
}


This guarantees that unlock_display() will be called when the function call exits. It doesn't matter if the code runs off the end of the function, or has an early return code added, or throws an exception, or uses a longjmp statement. It's guaranteed that the destructor will be called as part of the function's exit process.

This technique is incredibly useful. It can be used to ensure that all manner of paired operations occur. I would say that Wesnoth's rather good reliability can be attributed mostly to extensive use of this C++ feature.

No other language I know of provides this feature. Not Java, or Python, or C, or Perl, or Objective-C. Some, such as Java and Python do support the 'finally' keyword, but frankly, robust error handling with such languages requires a try...finally block around almost every non-trivial function, and it still doesn't come close to providing the functionality of C++ destructors. And people call C++ ugly?

C++ has several other big advantages -- its C compatibility means that there are numerous Open Source libraries available to easily interface with, and its standard library provides a nice framework to work with. It is also available on many platforms, and thanks to its use, Wesnoth is available for lots of operating systems.

C++ is far from perfect, and I hope someday there is a better language available, but my opinion from use of C++ is that it's the best language available today to program a medium size of large game project in. (For something small, Python, Ruby, Perl, etc would probably be okay).

Finally, I don't mean to start a flame war here. It's just that lots of people have asked "why C++?" and I thought I would put my thoughts down rather than having to explain to them each time. Even if you don't agree with C++ being a good language to use, at least you might learn something by reading some of the advantages I think it has. And finally, since you're here, you probably think Wesnoth is a good game, so do consider that apparently C++ can do something right. :)

David
"I made this letter very long, because I did not have the time to make it shorter." -- Blaise Pascal
Dave
Lead Developer
 
Posts: 6950
Joined: August 17th, 2003, 5:07 am
Location: California

Postby Casual User » June 25th, 2005, 4:11 am

Good afternoon!

Can't say I have anything important to say, but I read Dave's essay and something caught my eye.

I've had a little experience with programming (and if it would have been up to me, it would have been none...), mostly C and some C++, and I wanted to say I honestly cannot imagine writing a program like Wesnoth. Not so much because it's so hard (you can get over that) but because I can't stand programming... I saw Dave say he isn't very patient and I just can't buy that. When I finished my first project, I came very close to punching a hole in the wall because I was so, pardon the language, pissed off. So I can't imagine how many holes I would have punched by the time I would've programmed something like Wesnoth...

I don't think I've said it before, so I'll take the opportunnity to say that, especially for a non-commercial game, Wesnoth is great. I tend to be impressed by people who can do things I can't, and if those programming courses have taught me anything, it's that I'm good at programming and that I would never, ever be able to code anything worthwhile. Thus, I am impressed...

Liked the essay, and I was surprised to understand all of it...

Thanks for reading and sorry about the pointless post.
User avatar
Casual User
Forum Regular
 
Posts: 475
Joined: March 11th, 2005, 5:05 pm

Postby freecraft » June 25th, 2005, 8:40 pm

All I can say is --- python rulez ...

I like C++ only because of its speed. Everything else s*cks. Sorry I had to mention that word, but I really get pissed off every time I try to make something in C++. I don't know, I liked C++, but recently I have experienced few times how C++ can be stupid.

What I have started learning before one year is Python. In a month, I have learned stuff that I would need six to learn it for C++. Today, I have some skills in every library that exists for Python. Do you know what makes python so easy to learn? I think it is because of python's interactive console. Yep, python has really nice syntax, objects, philosophy --- everything --- but, for a guy who is trying to learn something, interactive console (or interpreted) is *great* thing.

Python is really simple and powerful, it is not hard like Java, and it is not "dangerous" like C++.

What I can say about python bad sides :
1. it is interpreted, which makes it slower than some other languages (even Java)
2. it is in heavy developement stage
3. libraries are made by opensource programmers, and you can never be sure that one day programmers who made it won't abandon it. That makes me sometimes very insecure, but, hey, it is worked until now afaik! It is in opensource philosophy!

And about gamming --- well, I think that I could never try making a game like wesnoth in python. Nope, I don't think pygame can handle so much sprites like C++ can. Butttt, it is not a helpless case. There are other libraries which can make game programming in python possible. For example, Panda3D is great 3d lib as I can hear from some people who are using it.

Still, as I said, python is really great and probably the best choice in everything except game programming. But IF I would make a game, or be a member of team who makes it, I would certainly do some things that aren't done in wesnoth:
1. I would make the whole server entirely in python --- pyyythooon
2. I WOULD add python scripting instead of writting my own WML like you did Dave (I know this has been discussed many times)
3. And, project's homepage would be done in python too, coz I hate php.

PHP:
"" == 0 : TRUE
0 == "": FALSE
-- enough for me ;)

I don't know even why I wrote this text here when it has almost nothing to deal with Dave's post, but maybe it is because I am surrounded with people that like VisualBasic, Delphi, Php, Java, BlaBla and nobody likes python. I just can't understand why. Why all customers (clients) want their webpage to be done in php and mysql? Why I can't offer them Postgre with Python? Why people want want their application to be done in C# instead in python with gtk ? Why? Why is python so unpopular? Damn, I like the snake! GG -_-
freecraft
 
Posts: 96
Joined: April 28th, 2005, 12:49 am
Location: Serbia

Postby torangan » June 26th, 2005, 9:42 am

I must say this is a rather nice explanation of the issue. Like the post from freecraft shows, not everyone understands it :wink: but it should cut down the number. I agree mostly about the argument of static type checking. The first language I learned was Perl and when I first had to write in C++ I found it annoying. Once you've worked on a project which is not counted in hours but in man years, you'll most likely change your opinion about that! It's very usefull and C++ IS a very nice language - AFTER you've spent a couple of years learning it. Yes, you need years to learn it and only weeks for other languages. So why bother? Well, you need so much time not because it's intentionally difficult but because it's an extremely powerfull language. You need to learn it's quirks and hidden corners in order to control a huge power and to know what to use when. If you think it's too much time, compare it to learning another profession...

PS: I totally dislike Python. I've got to code in it for my job right now and beside the usual problems with scripting languages, it has the unique disadvantage of whitespace indenting. This ranks among the most stupid programming paradigms I've ever seen. (No need to discuss this with me, I've had the discussion and I won't change my *personal* opinion. This is no objective critic but my position and yours may differ. No problem at all.)
torangan
Lord of WesCamp
 
Posts: 1365
Joined: March 27th, 2004, 12:25 am
Location: Germany

Postby allefant » June 26th, 2005, 12:23 pm

Yes, thanks, very enlightening post. Something I'm wondering, what do you think about Boost? Would it benefit Wesnoth to use it?

About Python, I do think the main reason against writing something like Wesnoth in it would be performance.

I'm actually quite sure you would have less safety problems in Python than in C++. Static type checking is no issue of course since everything is runtime checked anyway, and I think python's ref counting would also allow something like that auto-unlock trick since the GC would always call the destructor immediately - not sure though. (Of course, both examples show how much processing python does at runtime.) And there are many more examples where Python is much safer. I don't want to know how often I wasted time in C++ because of stupid coding bugs ("oh no, there was one possible constructor who would not initialize this variable").

And Python code just is much more easy to read and understand (my personal opinion). So I think, if I would try to write something like Wesnoth, I would do all the low level performance-critical things in C++, but WML and high-level (and only high-level) AI logic would be done in Python.

And then, I probably wouldn't.. I would try to design my own python-like WML, and never get anything done :P
User avatar
allefant
Developer
 
Posts: 477
Joined: May 6th, 2005, 3:04 pm

Postby Dave » June 26th, 2005, 12:54 pm

freecraft wrote:I don't know even why I wrote this text here when it has almost nothing to deal with Dave's post


That's my feeling too. :)

The big question I have for people who make posts like you do is, have you actually developed a substantially sized software product in Python? For a small project, sure, Python is great. It's projects of the size of Wesnoth or larger that I'm talking about here.

I'm not saying that Python can't or hasn't be used on such projects, but that there are a whole slew of issues that crop up on such projects that people who have only written small projects don't understand.

I like discussing programming issues with people, especially since I'm always looking for ways to develop things better. However, I'm not interested in getting into arguments with people who haven't developed on a big project, since they likely have no idea what they're talking about.

allefant wrote:Static type checking is no issue of course since everything is runtime checked anyway


Runtime checking is vastly inferior to compile-time checking imo. With compile-time checking the code is checked for all cases that could ever occur. With run-time checking, the code is checked only for cases you test with.

allefant wrote:nd I think python's ref counting would also allow something like that auto-unlock trick since the GC would always call the destructor immediately - not sure though.


I am rather sure it can't. I discussed this with a huge Python advocate who is one of the developers of Twisted Python once, and he agreed it couldn't.

allefant wrote:I don't want to know how often I wasted time in C++ because of stupid coding bugs ("oh no, there was one possible constructor who would not initialize this variable").


These are annoying, but part of 'learning C++' is to know how to hone in on these very quickly.

There are equally 'stupid' coding bugs in Python: "oh no, in this one obscure case we didn't test for, a string is passed to the function when it expects an int"

allefant wrote:And Python code just is much more easy to read and understand (my personal opinion).


I used to agree with this, until I had to actually maintain some Python code in a job I worked at.

The code was well-written, had a complete set of unit tests, and so forth. Yet I found it incredibly difficult to maintain. Why? It's just so difficult to tell what's going on -- can I call this function with this set of arguments? Does that function modify its arguments? What type is that variable? And so forth....all these things you can tell instantly in C++.

David
"I made this letter very long, because I did not have the time to make it shorter." -- Blaise Pascal
Dave
Lead Developer
 
Posts: 6950
Joined: August 17th, 2003, 5:07 am
Location: California

Postby allefant » June 26th, 2005, 6:56 pm

I am rather sure it can't. I discussed this with a huge Python advocate who is one of the developers of Twisted Python once, and he agreed it couldn't.


I don't know too much about this - but just tried the below code and it does the expected thing:

Code: Select all
class TestLock:
    def __init__(self, x):
        self.x = x
        print "init %d" % self.x
    def __del__(self):
        print "del %d" % self.x

def test(i):
    t = TestLock(i)
    print "test %d" % i
    # here t goes out of scope..

for i in range(100):
    test(i)


But well, not too much point in this test, only a python expert would know for sure.. so probably just a lucky case, or it's implementation defined. Maybe it also shows the problem with Python, it just works so nice and as expected in short scripts that you believe it always does the same. Like, I might now have relied on it in a bigger project, and then later, boom :|

These are annoying, but part of 'learning C++' is to know how to hone in on these very quickly.

There are equally 'stupid' coding bugs in Python: "oh no, in this one obscure case we didn't test for, a string is passed to the function when it expects an int"


Yes, true.. missing initializing of variables is just one point I find annoying in C++, since it's nr 1 on my list of mistakes I make all the time. Not that Python is that much better, but it would at least throw an exception when trying to access such a variable.

Probably if I would spend as much time coding in Python than in C/C++, I also would hit some re-occuring annoyances (so far the only one is that i forget the : constantly, resulting in a parse error).

I used to agree with this, until I had to actually maintain some Python code in a job I worked at.

The code was well-written, had a complete set of unit tests, and so forth. Yet I found it incredibly difficult to maintain. Why? It's just so difficult to tell what's going on -- can I call this function with this set of arguments? Does that function modify its arguments? What type is that variable? And so forth....all these things you can tell instantly in C++.


Very interesting point. I was mostly thinking of using Python APIs which are well documented or obvious enough to know how to use them, but my experience is mostly simple scripts (just interfacing well documented/defined APIs). So there's little if any program-internal API involved. But thinking about it, if Wesnoth would be written completely in Python, and had the same amount of comments it has now in C++, maybe I would have had a much harder time hacking around in it..
User avatar
allefant
Developer
 
Posts: 477
Joined: May 6th, 2005, 3:04 pm

Postby Dave » June 26th, 2005, 8:29 pm

allefant wrote:
I am rather sure it can't. I discussed this with a huge Python advocate who is one of the developers of Twisted Python once, and he agreed it couldn't.


I don't know too much about this - but just tried the below code and it does the expected thing:


The behavior depends on the Python implementation though. Jython for instance, will have different behavior. I'm not even sure all versions of straight Python will guarantee that it always works like this. Simply, it's not reliable.

Also, you can't really be sure that the object will be destroyed at the right time. If t is re-assigned to something else in the middle of the function, it will be destroyed too early. If some other variable is assigned to t, then it might not be destroyed until later.

What's more, it is easily possible for you to have some kind of circular reference in some kind of guard objects. Then they won't be destroyed until the 'mark-and-sweep' occurs.

The mechanism is not even close to as reliable as destructors in C++.

allefant wrote:Probably if I would spend as much time coding in Python than in C/C++, I also would hit some re-occuring annoyances (so far the only one is that i forget the : constantly, resulting in a parse error).


Probably. I used to relish the opportunity to work with Python until I had to use it in a large, production project. After that experience I am rather doubtful about it.

Simply, I know how to write non-trivial software in C++, and I've done it. I don't know how to do it in Python. It's obviously possible, but I can't do it.

David
"I made this letter very long, because I did not have the time to make it shorter." -- Blaise Pascal
Dave
Lead Developer
 
Posts: 6950
Joined: August 17th, 2003, 5:07 am
Location: California

Postby szopen » June 27th, 2005, 6:48 am

Very good post, Dave. I was making my personal project and I finally abandoned it, and one of chief reasons was expectation that people will sturm my mailbox and everyone will be happy helping programming. Few dozens of people actually appeared, butonly one of them actually was doing something, and few other were contributing only from time to time. When I then found wesnoth, i simply lost the interest in developing my own project and abandoned it.

For C++ it is very good language. It is flexible and powerful. It had a lot of libraries (STL, boost) which boost your productivity by magnitude (when i start my own project, i didn't know much about them, but right know I think the next one will rely heavily at least on boost :) ).
szopen
Forum Regular
 
Posts: 390
Joined: March 31st, 2005, 12:51 pm

Postby Invisible Philosopher » June 27th, 2005, 11:45 am

allefant wrote:Something I'm wondering, what do you think about Boost? Would it benefit Wesnoth to use it?

It would benefit Wesnoth to use Boost -- I would be happier contributing to the C++ if I could use it (okay, that's a selfish take on things (but it's true)). It has so many useful libraries, and the number continues to grow. (Some of the simpler and more frequently used libraries, such as some of the smart pointers, are probably going to be standardized... a process that takes several years, however.)
Play a Silver Mage in the Wesvoid campaign.
Invisible Philosopher
Forum Regular
 
Posts: 873
Joined: July 4th, 2004, 9:14 pm
Location: My imagination

Postby Dave » June 27th, 2005, 12:29 pm

Invisible Philosopher wrote:
allefant wrote:Something I'm wondering, what do you think about Boost? Would it benefit Wesnoth to use it?

It would benefit Wesnoth to use Boost -- I would be happier contributing to the C++ if I could use it (okay, that's a selfish take on things (but it's true)). It has so many useful libraries, and the number continues to grow.


We have discussed using Boost before. One of our developers is actually a Boost developer. :)

There was no firm decision on it either way, though I do tend to think that if we were to use it, we'd only end up using a relatively small subset -- smart pointers being the biggest thing. There was alot of suggestion that it might be easier just to import their smart pointer implementation into our code base.

David
"I made this letter very long, because I did not have the time to make it shorter." -- Blaise Pascal
Dave
Lead Developer
 
Posts: 6950
Joined: August 17th, 2003, 5:07 am
Location: California

Re: C++ and games programming: the Wesnothian Approach

Postby Rhuvaen » June 27th, 2005, 1:42 pm

Fantastic post. I hope this will make the younger generation of programmers-to-be become more aware of the benefits of hard study compared to using languages that offer quick results. :lol:

I am hardly a programmer worth his salt, and certainly not with any formal training (so forgive me for phrasing things incorrectly, should I do that), but have worked professionally on projects in Java and Python and on private projects in C (with a smattering of C++) and Ruby. The good part of this is that I'm not really married to any particular language :wink:.
I can only agree with your opinion of static type checking. At first the greater freedom of just passing anything as an argument and then dealing with it seemed intriguing. But, digging through several thousand lines of Python code trying to find all instances of a method call to see what possible types are actually passed in an argument is most annoying. In the end it often proved easier to rewrite some already existing functionality from scratch using exactly the arguments I needed. I guess if you're working on a project where at some point you'd expect some people from the outside to join the project development, you're going to have a hard time without static type checking.

I think it's much more readable to overload a method regarding the type of it's arguments (rather than doing type checking in the method code) - in Python that's impossible since you can only overload by having a different number of arguments.

In Java, you can use "interfaces" as definitions of methods and argument types and people can work independently on all sides. Java is probably the most bureaucratic language of all, because a much higher percentage of the code you type is just used for definitions and code structures. This truly is ugly, because it makes you search for the significant bits of programming logic among pages of structural code. I guess this follows pretty much the design process (as you design the structure and flow of data first), though, but it does tend towards letting people spend more time on designing the software architecture rather than seeing whether the nitty-gritty stuff will work out as they plan it to.

Dave wrote:in C++ one can use the const keyword to promise that an object won't be modified

In Java, you use the final keyword in just the same way to do that. Or is there something I'm missing here?

Dave wrote:Understanding how const works exactly is one of the many difficult aspects of C++ to master, but once it's second-nature the time-savings are impressive with the number of errors the compiler spots for you. (...)
In fact, even a language like Java is more or less eliminated, since it's static checking isn't nearly as comprehensive as C or C++'s.

That must be true, since it's the C compiler that most often defies my C skills :D... Seriously though, it seems that with C++, the different compilers seem to be varying in their degree of rigorousness. So, rather than enforcing a rigorousness in exception handling etc through the language, which at least ensures consistent code on different platforms (never mind how the particular interpreter actually executes it :wink:), isn't there a problem with C++ in that code that passes one compiler fails in another? This lack of consistency always gives me the feeling that the problem is actually getting the compiler to understand my code, rather than there being a problem with the code itself :P.

Can you recommend some good reading for people with OO skills, but who fail in knowing the details of C++ type checking, pointers and so on?

Dave wrote:There is another advantage of C++ though, (...) the presence of destructors.

Operations in programming often occur in pairs. You allocate memory, then later you release memory. You open a file, then later you close it. You lock a mutex, then later you unlock it. You begin an operation, then later you either commit it or roll it back.
(...)
Now, of course, many languages solve this problem through garbage collection. That is, the program doesn't have to free memory allocated, the language will use some mechanism to automatically detect when it is no longer used, and reclaim it.

This is fine as far as it goes, but memory allocation and deallocation is only one of many paired operations in programming. How about opening a file and closing a file? Or opening a socket and closing a socket? Most languages with garbage collection do support operations known as 'finalizers' which are called when an object is to be destroyed, and they could close sockets and files, but they have a serious problem in that the languages make no guarantees of when they will run -- or even whether they will be run at all.

I just checked this in my old Java books and what you say seems to be true! :shock: There are no such things as objects only existing within a given scope... Their handles do, but the object could be freed from memory much later by the garbage collector!
As you said, in Java you'd catch an exception from the crucial code and try to clean up with "finally", but honestly, I haven't seen a lot of programmers who were very rigorous with their exception handling. It also requires that the programmer anticipates the problem.

Still the problem remains of when to free up the memory associated with objects that are accessible to different parts of your code, and I guess there's no language-specific solution for that.

Having said all that, I do like Ruby very much for the way in which its interpreter is very flexible, and that in a consistent way (unlike Python). A language that is flexible in adapting to the particular style of the programmer will probably never make it into team projects, though, for obvious reasons :). And of course you can only find bugs at run time, with few tools (if any)... I've taken a tentative look at Groovy, though, which is the best parts of Java married with the flexibility of Ruby, and it might just be the language of choice should I ever get back to my project ideas. Unless I get to learn C++ in the meantime, that is :wink:.
Rhuvaen
Developer
 
Posts: 1272
Joined: August 27th, 2004, 8:05 am
Location: Berlin, Germany

Java and Type Checking

Postby KaptinBadrukk » June 27th, 2005, 7:40 pm

My experiences with Java for game development is pretty limited, but I've been doing commercial (web) development for a few years, so I'm bit confused about why/why not you found Java to be unsuitable for game development. Admittedly, C++ is hugely more common for commerical development (with Java being virtually non-existant).

This is probably philosphical debate, but I'm a huge fan of unit tests, and once you ingrain the concept (especially Test First programming) it pays huge dividends, especially when you start refactoring. But I digress already.

Type Safety
I'm much less familiar with C++ type checking, but I'm assuming you are refering to templated/type safe containers. Its true in Java that a container (LinkedList, Map, HashSet) can hold any object, forcing manual casts and theorically leading to bugs like this.

Code: Select all
List units = new ArrayList();
units.add(new Unit("Wizard"));
units.add(new Food("Steak"));

// Much later

// Works fine
Unit unitOne = (Unit)units.get(0);

// Throws ClassCastException as Food isn't a Unit
Unit unitTwo = (Unit)units.get(1);


But in practical terms this bug virtually never seems to come up. The addition of Generics in Java 5 is supposed to solve this, but think it just makes things more complicated than is necessary. Putting Food into my Unit Containers just doesn't account for enough bugs to make the complexity necessary. See Bruce Eckel (a C++ and Java guru) here for more details http://www.mindview.net/WebLog/log-0061.

Memory deallocation vs. Resource freeing
You mentioned C++ destructors vs. Java 'finally' key word. A closer analogy would be Java's use of the Object.finalize() method. Java developers should almost never never never use finalize() because it (unlike destructors) isn't guarnteed to be ever called. If you need to close a resource (like a database connection or filestream), you need to use the finally clause, which while, messy does work. Adding closures to the language would greatly assist this (which Ruby and Python have... I think) That said, the biggest advantage of Java over C++ is memory management.

My personal belief is that Garbage collection is a HUGE productivity boost for languages that have it. It elimates a huge class of errors (i.e. memory leaks) that makes life tough on non-GC languages. You still have to deal with other resources of course, so it no panecea, but hey, one thing at time.

Now, I plead ignorance about how will GC works for a games, only because of the effects of pausing/etc. But I'm curious enough to wonder if anyone else out there has tried it.

Anyway, thanks for the great game, as well as insights into its development.
KaptinBadrukk
 
Posts: 1
Joined: June 27th, 2005, 7:14 pm

Postby freecraft » June 27th, 2005, 8:47 pm

It is hard to reply on theese posts. Mannnny questions will remain opened.

But lets just mention few of them.
a) Some of you guys said that it is hard to identify what thing you should pass as an argument to some function?
A: Silly! As I said, python programmer with good programming style (must have) would write *all* functions that have to be used outside from the local object, with documentation string. Example how do document functions and how to change between types:

Code: Select all
class A:
    def _private_fun(self, a,b,c):
        # do some job, explained in short words

    def setImportantParameter(self, p):
        """
            Set the most important paramete in class.
            Watch out, p MUST BE A STRING :)
        """
        self.p = p

    def setInPythonWay(self, p):
        self.p = str(p)
        # or
        if isinstance(p,string):
            self.p=p
        else:
            self.p=str(p)   


Programmer can easily type help(A.setImportantParameter) and get the explanation. Only bad programmers don't write documentation, and they aren't considered to be python programmers - so, your argument fails.

b) Java has interfaces
A: The reason java has interfaces is because it hasn't multiple inheritance. However, even then, interfaces are good because they are used in Adapter pattern. For this reason, interfaces are developed by Zope.org team and they are used in twisted - and I use them too in some of my "small, private projects" ...

c) Python is bad for big projects
A: There are two possible answers.
first: python makes big projects to be small - yep, do it in pythonish way and - whoops - it becomes smaller.

second: Big projects are made by lot of people. Right? And you couldn't understand what was your part in that project, Dave? My opinion is that you should know what is your part of the job, and then you can program just like you are working with any other programming language --- the point is, I don't beleive you couldn't work with that python project because of python, but rather because you hadn't enough "documentation" - dunno, uml diagrams, somebody who can explain you the things going on etc

d) Python has problem with "spaces" and "portability"
A: No, it doesn't. Theese problems are made by bad Code Editors (programs in which you type python code ;) ). I know this from my experience. GEdit is bad for py source editing. IDLE is perfect on the other hand.

e) Python is hard readable in big projects
A: Big projects shouldn't be read. I think so, because I had experience in Java programming, and, damn it, I had to figure out all by myself what to do. The source was huge, and I was supposed to make the input interface and connect the listeners to correct components. I have almost died. Now, is Java hard readable, or it is my "employer" guilty because he hasn't gave me project plan or anything else except the RAW source?

To many other thing --- Python is not for game programming, as I said. I don't know why are you blaming python, this is not python topic. Hm, my opinion is that C++ is the best PL for games. For most other things - python rulez - sorry.

OneMoreThing: Python isn't changing the way "big" projects are made. Patterns and OOP are still used. The "programming logic" remains in it abstract form. But the way you "type" your code, and amount of worryness about the code's safety changes.

Read this: I don't dissagree completely with any of you. It is just "an opinion". Dave is a legend, we all play his game and I do respect all of you guys. What I think is that you haven't "played" with python enough.
freecraft
 
Posts: 96
Joined: April 28th, 2005, 12:49 am
Location: Serbia

Postby Dacyn » June 27th, 2005, 9:52 pm

freecraft wrote:Only bad programmers don't write documentation, and they aren't considered to be python programmers

This is an odd argument IMO, although I know nothing about Python. The difference seems to be that in C++ the documentation is checked by the compiler, while in Python it isn't... how is that good? Anyway, it is much easier to write:
Code: Select all
string p;

than
Code: Select all
        """
            Set the most important paramete in class.
            Watch out, p MUST BE A STRING :)
        """

Edit: Basically, I agree with Dave on the next page...
Last edited by Dacyn on June 28th, 2005, 3:28 pm, edited 1 time in total.
Dacyn
Forum Regular
 
Posts: 1855
Joined: May 1st, 2004, 9:34 am
Location: Texas

Next

Return to Game Development

Who is online

Users browsing this forum: No registered users and 0 guests