Wondering why ID choosed C for Quake 3 instead of C++

5b1e3128b33fa994d6c6640fb49898da
0
jokoon 101 May 10, 2010 at 16:27

Hello ! :)

First, I’m not a very experienced programmer, but I know C and C++, maybe not the advanced stuff.

Since ID software released of the quake 3 source code, I was wondering why they used C instead of C++.

Is there a real advantage of working with plain C, meaning not dealing with abstract stuff ?

I know the object concept can be great, by I never really understood the real advantage of C++, since it depends of the programmer’s way of solving a problem…

I already asked on a french forum (which was closed for reasons I don’t really know, maybe they could not really answer, but apparently they did not like my tone, you know the french…)

Thanks for reading :)

30 Replies

Please log in or register to post a reply.

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 May 10, 2010 at 16:42

The Quake3 engine has been progressively developed for a very long time. I remember reading an interview with some guy working at id (can’t remember if it was Carmack), and he said that it still contains code from the original Doom game, which ran an very early version of the engine. The engine for Doom was again an improved version of the Wolfenstein 3D engine. C would have seemed like a better choice of language back then, I guess.

You can download the sourcecode for Doom, and compare for yourself :)

As for using C++ over C, features like templates and dynamic binding make it easier to use, imho.

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 May 10, 2010 at 16:48

I think Carmack prefers c. OO programming does add a layer of abstraction, so it might be a little slower, depending how it’s used. Most engines use c++ now because it’s better for code organization and the speed difference is minimal.

17ba6d8b7ba3b6d82970a7bbba71a6de
0
vrnunes 102 May 10, 2010 at 17:55

Some years ago C was still way faster than C++, because each processor clock cycle were very important.

8fd4a055522ce713cde7dd1cb4083cb2
0
martinsm 101 May 10, 2010 at 18:42

Because C++ (standart, not the actual compiler!) was properly released only 1 year before release of Quake 3. If you take into account that developing it took 1 or 2 years, then you must undesrstand why they didn’t choose C++.

A77e71b962cd6c7c3b885f0488452f1f
0
tobeythorn 101 May 10, 2010 at 18:45

In addition, some reasons might be:
1. c may have been more portable than c++ at some point

  1. without message passing, c++ isn’t really objected oriented anyway, and one can write fairly object oriented style code in c.
340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 May 10, 2010 at 23:27

@martinsm

Because C++ (standart, not the actual compiler!) was properly released only 1 year before release of Quake 3. If you take into account that developing it took 1 or 2 years, then you must undesrstand why they didn’t choose C++.

C++ compilers existed well before the standard was released. Quake 1 was developed using DJGPP, which uses a DOS port of the GCC compiler, which was a pretty decent C++ compiler at the time. Quake 3 was developed using Microsoft Visual C++ 6, which, as the name suggests, also supported C++.

The reason they were using C was performance considerations and simply because “they’ve always been coding C”.
@tobeythorn

  1. without message passing, c++ isn’t really objected oriented anyway

I’m sorry, what?! The main pillars of object oriented programming are data abstraction, encapsulation, polymorphism, and inheritance. C++ supports all those features. Message passing has nothing to do with OOP per se. Sending/receiving messages and calling/being called by methods serve exactly the same purpose.

A77e71b962cd6c7c3b885f0488452f1f
0
tobeythorn 101 May 11, 2010 at 00:53

.oisyn,
In my opinion, message passing is a requirement of encapsulation. If telling a object to do something that it doesn’t understand can break a program, then the object isn’t completely encapsulated. Furthermore, since c++ doesn’t natively support reflection or dynamic typing, the programmer must always be aware of exactly what class each object is, which doesn’t seem like pure encapsulation to me either. The point is that c++ really isn’t much different from c, just sometimes more convenient.

In c we would write…

Rabbit* myRabbit = rabbit_new();
rabbit_run(myRabbit);

In c++ we would instead write…

Rabbit* myRabbit = new Rabbit();
myRabbit->run()

Are these not exactly the same?

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 May 11, 2010 at 01:51

You could just as well say that writing

|myRabbit|
myRabbit := Rabbit new.
myRabbit run

in Smalltalk is exactly the same as the two examples you posted. Does it really matter whether you call what’s going on “message passing” or “calling a method”? It’s all the same when you get down to it.

IMHO, the core concept of object orientation is that some data (an object’s state) and code (its behaviors) are tightly integrated and presented as a unified component (an object) to the outside world. Languages can support this model to a greater or lesser extent. C++ doesn’t fail to be object-oriented just because it doesn’t take the idea to some (il)logical extreme.

A77e71b962cd6c7c3b885f0488452f1f
0
tobeythorn 101 May 11, 2010 at 02:07

Reedbeta,
No, I don’t think so. In all three example the grammar is essentially identical. However, the underlying behavior of message passing is different, even though it does involve a function call. In contrast, the underlying behavior of both the c and c++ example I gave is the same.

I think that your definition of object-orientation is both fair and de facto. C++ is then clearly an object orientated language. However, I think most people do include encapsulation in their definition.

820ce9018b365a6aeba6e23847f17eda
0
geon 101 May 11, 2010 at 10:00

“If telling a object to do something that it doesn’t understand can break a program, then the object isn’t completely encapsulated.”

On the other hand, if your compiler *let* you pass a message the object doesn’t understand, it could easily break your program.

I have been working with Objective C the last few weeks, where you call methods in objects and the runtime decides if the method is supported or not for the speciffic object.

I really can’t see how it is any better to use lots of void* (which is what Objective C does) instead of base class pointers, where you *know* exactly what methods you can call.

3c5be51fdeec526e1f232d6b68cc0954
0
Sol_HSA 119 May 11, 2010 at 10:10
  1. C is *still* more portable than C++.
  2. It’s possible to understand everything about C. I’m not quite sure if the same can be said about C++. It feels like I keep forgetting more obscure things about C++ than I learn.
  3. C++ has plenty of hidden overheads that also may vary from platform to platform or compiler to compiler.
  4. Since C is older, the compiler optimizations have had more time to mature.
  5. Preferences.
  6. Basing designs on C++ doesn’t mean they automatically become easier, clearer, faster to develop for. Just look at Symbian.

Before I moved from C to (majorly) C-With-Classes C++ coding, my code was already turning very much class-like (storing states in structures that were passed along etc). If C++ had not been available to ease my typing, I’m pretty sure I’d continued the same route.

Fd80f81596aa1cf809ceb1c2077e190b
0
rouncer 103 May 11, 2010 at 10:56

Maybe Carmack just hates object oriented, i dont use it either. ;)

886c9946a5dd9887a33d237ddcc9b7e7
0
phresnel 101 May 11, 2010 at 12:36

@Sol_HSA

  1. Since C is older, the compiler optimizations have had more time to mature.

I think that argument is mostly void, and the contrary is the case: Because C++ is mostly compatible to C, a lot of C++ code can benefit from those optimizations. And because C++ offers additional and very powerful tools, it has even more stamina in optimization.

820ce9018b365a6aeba6e23847f17eda
0
geon 101 May 11, 2010 at 15:09

@rouncer

Maybe Carmack just hates object oriented, i dont use it either. ;)

Actually, according to Carmack himslf the Quake I/II/III engines are supposedly written in an object oriented “style”.

I’m not really sure what that means.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 May 11, 2010 at 15:42

@tobeythorn

.oisyn,
In my opinion, message passing is a requirement of encapsulation. If telling a object to do something that it doesn’t understand can break a program, then the object isn’t completely encapsulated.

Nonsense. Programming by contract or not has nothing to do with OOP. Also, allowing the compiler to catch errors the runtime would otherwise catch does also have it’s advantages.

Furthermore, since c++ doesn’t natively support reflection or dynamic typing

C# does, which uses the same method calling paradigm as C++.

the programmer must always be aware of exactly what class each object is, which doesn’t seem like pure encapsulation to me either.

Yes it is. Because the programmer doesn’t need about the actual implementation of the object. It only needs it’s interface. Which has it’s advantages, because you know which “messages” it understands and what parameters you can give those messages.

Besides, it would be very easy in C++ to devise objects that actually uses message passing rather than method calling, if you find that more attractive. But that doesn’t make it any more OOP.

The point is that c++ really isn’t much different from c, just sometimes more convenient.

That’s why OOP is a design paradigm, and has nothing to do with syntax. You can design an OOP program in C just fine.

In c we would write…

Rabbit* myRabbit = rabbit_new();
rabbit_run(myRabbit);

In c++ we would instead write…

Rabbit* myRabbit = new Rabbit();
myRabbit->run()

Are these not exactly the same?

As is with any language. What’s your point?

17ba6d8b7ba3b6d82970a7bbba71a6de
0
vrnunes 102 May 11, 2010 at 15:49

But answering again to the original poster =), I still think the only reason was raw performance, because at that time processors were a lot slower than today processors, and Mr. Carmack knew that.

Before that, ASM was the main language of choice for games.

A77e71b962cd6c7c3b885f0488452f1f
0
tobeythorn 101 May 11, 2010 at 16:03

My point is that I don’t see much motivation for migrating from c to c++. In contrast, message passing, whether you like it or not or consider it an import or optional part of OOP actually has functionality beyond a simple function call.

Yes it is. Because the programmer doesn’t need about the actual implementation of the object. It only needs it’s interface.

In non-message passing environments, if I have an array [snake, elephant, bird] and want to call a function move() on each element of the array, I can’t without an unnecessary mess, without explicitly casting (which is just a fancy way of calling an explicitly different method for each), which means that I really do have to know about the guts of the objects, and not just their interfaces. Sorry, but to me that is just not encapsulation.

2b97deded6213469bcd87b65cce5d014
0
Mihail121 102 May 11, 2010 at 16:26

They could’ve wrapped the archaic C code in C++, no problem at all. I guess Carmack & Company like C m0ar, just as I do. If I have the choice between C++ and C, I would never pick C++ for anything in the world. Being simple, easy, well-documented and portable C has served me well over the years. For really large projects I would probably pick a MDE-approach with a bytecode language such as C#.

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 May 11, 2010 at 16:38

In non-message passing environments, if I have an array [snake, elephant, bird] and want to call a function move() on each element of the array, I can’t without an unnecessary mess, without explicitly casting (which is just a fancy way of calling an explicitly different method for each), which means that I really do have to know about the guts of the objects, and not just their interfaces. Sorry, but to me that is just not encapsulation.

That’s just using an abstract. Every OO language does that. You don’t recast, you just put all the objects in a base class container and call the abstract function.

46407cc1bdfbd2db4f6e8876d74f990a
0
Kenneth_Gorking 101 May 11, 2010 at 16:42

@tobeythorn

In non-message passing environments, if I have an array [snake, elephant, bird] and want to call a function move() on each element of the array, I can’t without an unnecessary mess, without explicitly casting (which is just a fancy way of calling an explicitly different method for each), which means that I really do have to know about the guts of the objects, and not just their interfaces. Sorry, but to me that is just not encapsulation.

That is just nonsense. The only thing you need, is a shared baseclass with a virtual ‘move’ function, and nothing more. Your code does not need to know about [snake, elephant, bird], and you certainly don’t need any casting.

A77e71b962cd6c7c3b885f0488452f1f
0
tobeythorn 101 May 11, 2010 at 16:57

That is just nonsense. The only thing you need, is a shared baseclass with a virtual ‘move’ function, and nothing more. Your code does not need to know about [snake, elephant, bird], and you certainly don’t need any casting.

Yes, and then we need to make everything derive from the same base class, even when it doesn’t make sense to.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 May 11, 2010 at 17:06

@tobeythorn

Yes, and then we need to make everything derive from the same base class, even when it doesn’t make sense to.

Well, everything that has a move() method can implement an IMove interface (in C++, implemented as an abstract class with no data members), if you want to be able to make move() calls virtually. And an object can implement as many or as few interfaces as necessary.

I admit this would be cumbersome if you needed to have a large number of small interfaces. So maybe from a syntactic perspective, an explicit message-passing language like Smalltalk would be nicer. But the point is, you can certainly do proper encapsulation in C++ if you want.

A77e71b962cd6c7c3b885f0488452f1f
0
tobeythorn 101 May 11, 2010 at 17:17

Reedbeta,
Interesting, I learn something new every day!

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 May 11, 2010 at 17:18

Yes, and then we need to make everything derive from the same base class, even when it doesn’t make sense to.

Myself, I consider this a better programming practice because it makes you think about it. There will always be natural divisions in what you are doing. I’ve done things like entity, moving entity, friend, enemy, etc. and had containers for all of them with some abstracts going back to entity. Java and C# do this by deriving all classes from a base object class. That’s really what OO programming is all about as far as I’m concerned. C++ does things that Java doesn’t do like multiple inheritance. I don’t like it myself. C++ is a full OO language, it’s just a little screwy because it was derived from c, not as well organized.

Fd80f81596aa1cf809ceb1c2077e190b
0
rouncer 103 May 11, 2010 at 17:27

@fireside

… Java and C# do this by deriving all classes from a base object class. That’s really what OO programming is all about as far as I’m concerned…

Thats what they teach you in your beginning java tutorials, how to extend classes making more than one type of the same class so you can instantiate multiple classes from a single class reference.

I never learnt any more than that, im not a big OO guru, just enough to let me be able to code something largish, which is what c++ (oop) was invented for right? So you could have a largish program and still be able to add to it and maintain it without confusing yourself so much.

A638aa42130293f319eda7fa4ba121f4
0
fireside 141 May 11, 2010 at 17:42

I never learnt any more than that, im not a big OO guru, just enough to let me be able to code something largish, which is what c++ (oop) was invented for right? So you could have a largish program and still be able to add to it and maintain it without confusing yourself so much.

That’s what it does for me, anyway. I used to write spaghetti code until I found out about OO. It really kind of forces you to be more organized.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 May 11, 2010 at 22:51

@Reedbeta

I admit this would be cumbersome if you needed to have a large number of small interfaces.

Well I’m with fireside on this. More cumbersome is not always a bad thing. It certainly forces you to think more about your design, which, in the end, makes it simply better. Duck typing and such attracts more cowboy programming.

Fd80f81596aa1cf809ceb1c2077e190b
0
rouncer 103 May 11, 2010 at 23:02

yeeha!

3c5be51fdeec526e1f232d6b68cc0954
0
Sol_HSA 119 May 12, 2010 at 08:12

Writing C in “c++ style” is actually rather simple. Basically you need to pass a structure to functions.. like:

VidDataType *v;
v = load_vid("foobar.vid");
init_vid(v);
while (!is_vid_done(v))
{
render_vid(v);
display_vid(v);
}
destroy_vid(v);

SDL & co take it slightly further and include function pointers inside structures. Some oddball platforms even have inheritance of sorts done via some macro trickery, but I wouldn’t go that far. I’ve even seen some stl-like containers done with macro trickery *shiver*.

5b1e3128b33fa994d6c6640fb49898da
0
jokoon 101 May 12, 2010 at 08:22

Well thanks for the answers !
Seeing the little debate over OOP, I guess that is a good reason carmack didn’t choose C++