I had a whole reply ready, but IMHO it is not worth the trouble replying to.
"Oh, I wrote a reply, but I don't want to paste it because you're not a good person and I don't want to." My eight year old son knows that nobody falls for this sort of passive agressive dismissal; it's disappointing that you do not.
Namedropping doesn't make you seem correct, y'know.
Neither does getting personal about perceived faults, when it's pretty much your own assumptions that are the problem.
Observing that something you've done isn't effective is hardly my getting personal. Believe me, there's no shortage of material; what I said above about my son is, for example, personal, as is the following: turn down the whine knob until you've got something worth saying to say.
So the whole template rant is bogus.
Given that it was not you but the original poster which set the domain of important languages, and given that I also touch on pure-C and pure-Java issues, this protest is as bogus as it pretends what it's attacking to be.
- When I am talking about state, I'm talking about state in the compiler. (which in most _performing_ tools has the preprocessor built in btw)
Yes, I heard you the first time. The reason I referred you to modern c++ design is that you're wrong, and I have neither the patience nor the kindness to explain it to you. Start with section 3.5, or with any page explaining how C++ template metaprogramming is a functional language rather than an imperative language. Before you fly off the handle talking about how you weren't referring to templates *again*, please realize that the observations regarding template MP as a functional language in fact apply to everytihng in the C and C++ preprocessors.
Do not reply until you have read; repeating ignorance is no more argument than repeating falsehoods.
-- by {$i} ({$include in delphi), like #include in C
Actually, you're shooting yourself in the foot here. You're attempting to make the hasty generalization that there are two approaches to bringing outside code into a local place, that C/C++ advocate an "inline header system" (whatever the hell that is) and that Delphi does something different.
What you seem to fail to understand is that uses is a call to the Delphi linker; it is literally the same thing as the linker in C++, and in fact if you take the time to look at borland's BPIL, you'll find that they generate the exact same intermediate language binary. Furthermore, the very same examples you give, {$i} and {{$include}}, are the same as #include. Furthermore, both languages offer still other mechanisms to bring code in or to generate code.
That said, talking about Pascal's differences with C and then discussing what Delphi does is roughly equivalent to describing what Objective C does. Delphi is not pascal any more than Objective C is C. They are distinct languages. Delphi is Borland's third pascal variant, Object Pascal, which follows both Borland Pascal and Token Pascal (the last of which is so old that you pretty much can't find references to it online.)
Please don't lecture to me about Pascal; my use of Pascal predates Borland's very existence.
The main reason, and the fundament of a unit system, why the second way is more optimal than headers, is that the compiler reinitialises before reading a unit interface.
Uh. The pascal unit system is simply an in-code linking mechanism. It's no different than rolling your source together with your makefiles; if you'd bothered to read Wirth's papers on the design of the language you'd find out that Wirth himself suggests that "unit" is nothing important, and pretty much just syntactic sugar.
Now, how the compiler "reinitializes" before reading a unit interface is a little bit beyond me: the unit interface is just what a C++ programmer would call a collection of vtbls. Would you be willing to point me to any point
From someone whose URL involves the word "guru," almost certainly without having earned it, to attack a set of reasoned arguments with underpinnings with a nasty comment about appearances is just pathetically self-ignorant.
Wrong. If the compiler sees that a "global" (ie. the outermost scope) variable, a is declared static, then it knows that the value of that variable cannot be changed by other code outside the current translation unit.
A variable which isn't extern already has this facet. There is no change once you introduce file scoping. Don't be silly.
That might enable some optimizations which would not have been possible before (in particular, it may be possible to put the value into a register and keep it there for longer).
Uh. You really think that a variable might be left on the register for a different amount of time based on instructions to the linker? Have you yet realized that if you file-scope a variable and recompile you get the exact same assembly, and that like namespaces, file-scoping has literally no effect on the binary, that it's entirely a language-level safety for the programmer?
I would say that it is. Mathematically, if you convert (a+b/c)bc into abc+bb and then to b(ac+b), I call it refactoring, since it involves moving factors around.
Neat how you avoided the justification I gave, and instead relied on equivocation to make your argument for you. It doesn't really matter that in mathematics you can push things called factors around; that has nothing to do with Fowlerian refactoring. Should you choose to read his book, you may be surprised to learn that he himself places great emphasis on the distinction between modularization and refactoring.
n programming I say "refactoring" to mean "extract interfaces from your code and use them to pull up modules, which can then be encapsulated into classes".
1) That is an extremely specific case of modularization which happens to also be a case of refactoring; this is a fallacy of composition, wherein you are suggesting that because one extremely limited case is in both sets, then the two sets are equivalent.
2) It doesn't matter how you use the word; that's just not what the word means. I might say "I use the word rape to mean planting flowers on highways;" that doesn't mean that I may then engage in discourse about landscaping with the word rape.
3) Refactoring is a hell of a lot more complex than hand-encapsulation.
It will have a great deal of effect on the problem at hand, which is the increasing the speed of developer builds. If your modules don't let their intrails hang out, changing them does not require rebuilds anywhere outside the module. This is the only real way to solve the problem.
In the case of code with extreme coupling problems, this will actually make things worse. The problem is that coupling is the result of code swatch A requiring knowledge about code swatch B. Encapsulation does not decrease coupling in any way; it simply hides non-coupled parts from one another. Whereas that is certainly a Good Thing (tm), it will not address issues of coupling. Rather, it will force the programmer to bring more interface knowledge to various points within the program, increasing coupling, and thereby deepening the problem wherein code A requires a rebuild of code B to run, even though code B is unchanged. This is why I suggested Alexandrescu-style functors: they have an immediate decoupling effect.
No, this will not have any effect on the problem at hand.
Well, yes; I should have made it more clear that I meant static functions in C, which simply mean that the named function is not used outside the file.
Oh yes, I should have made it clear that I meant a crib mobile which moved itself, not a car, when I said automobile. You should realize that statics are static variables, and that static functions, despite having static in their name, are not statics at all.
The other important use of static is for const arrays inside functions, which should be declared static to allow the compiler to put them in the data segment at compile time instead of building them whenever the function is entered.
Static doesn't have any such effect on UDTs, and on machine types that already happens. This is myth: static local arrays are not in any way faster than nonstatic local arrays. Please try profiling such code in a relatively modern compiler; you should see absolutely no difference at all, like I see with gcc 3.1+, bcc 5.5+ and mscc 7.1.
As for threaded code, I never write any
Obviously. You also clearly don't write libraries, where you cannot guess about the nature of outside code. Furthermore, you plainly don't write code which is maintained by others.
event-driven asynchronous designs give much better and faster results
Proper logical and physical encapsulation can speed up builds by a huge factor.
True. That doesn't make statics encapsulation at all, let alone proper.
The more extreme encapsulation of pimpl can speed builds even more.
It's always a shame when people talk about sutter's pointer to implementation or fast pointer to implementation idioms without actually understanding them. Pimpl is not encapsulation. Encapsulation is the seperation of implementation from interface, the distinction between "inside" and "outside." Pimpl does not change what is inside or outside; it simply moves the implementation's guts deeper down in order to make operations like copying and pooling faster.
I think he is using 'static' in the sense of 'class variable' or maybe as in 'compilation unit scoped', not in the sense of 'persistent between invocations of a function'
I should hope not: class statics and local/global statics are not distinct in any way, and in context file statics don't make sense.
the third use of 'static' (local statics) is the one that always causes grief when threads enter the picture.
Uh. Consider a singleton flyweight head which has two threads attempting to add to the same weight at the same time. Even though it's a "class variable," there's a race condition.
You could conceivably cause thread problems with class variables, but to my mind that nearly always involves misuse of class variables.
Explain to me please a legitimate use of a class static which does not threaten race conditions absent locking or mutexing. Five points if your explanation does not hinge on a program which is single threaded by nature, making the example utterly useless.
but many of the problems that require massive overhaul can be avoided by the mutable keyword.
No, they can't. Digging yourself deeper into incorrect const behavior is not a solution to earlier const correctness behavior. You would do well to dig around the c2 wiki, where these issues are discussed at great length.
They certainly are not. File scoped variables aren't introduced into the external namespace. Besides, class statics should almost never exist; they are useful for shared buffering, singleton behavior, and little else.
You shouldn't be abusing naming mechanisms, scoping mechanisms or generative mechanisms in order to isolate a variable from the outside world. Make a private member or a singleton instead.
Also, please stop swearing when someone gives you constructive criticism.
In the second case, you need some serious refactoring.
Decoupling isn't refactoring. Fowlerian refactoring is about changing the interaction of code, the code paths, and pushing behavior around into conceptually correct normalized areas. Decoupling is just about specifying no more than the bare minimum information required for external linkage in order to reduce cross dependencies.
Encapsulate everything you possibly can. Make stuff private
Whereas these are good principles, it will have zero effect on the problem at hand.
and static.
No. Being static can introduce dozens of subtle bugs, particularly in threaded or reentrant code; unnessecary staticism is probably the single largest source of race conditions in C.
Furthermore, this not only has no effect on compile time, but for primitive types doesn't have the speed increase effect most people seem to expect. Whereas it is true that this prevents a construction and destruction pass during an exit and entry, for primitive types that time is zero in any decent compiler anyway. By contrast, static UDTs are frequent sources of bugs which are generally too subtle for people which make this mistake.
Static is a qualifier with important effects, like volatile, const, export and extern. To apply it in order to squeeze compilation speed is simply incorrect; a static variable is not the same thing as a normal variable in exactly the same way that a volatile or a const is not the same as a normal auto.
Make everything you don't modify const.
Yes and no. Const correctness is a deeper issue than this. Whereas things should be const by default, there are times at which constness is not conceptually correct despite that you won't be altering the variable in this particular use. In those cases do not apply const: not only does it enforce strong and contextually inappropriate strictures on users of the code, but can cause massive maintenance nightmares when you finally get around to using the mutable behavior of said code, and when during the repair of the const correctness mistake you discover that you have to overhaul 20% of your source base dependencies.
Running this way will annoy your compiler, your linker, your debugger, and every other link on your tool chain, and muck up many standard C coding practices.
Conspicuously absent from this list is "other programmers."
Yeah, because clearly designing with a careful eye for scoping and without obvious abuse of inclusion mechanisms is exactly the same as design by contract, and in fact has anything to do with design accretion or preprocessor hacks.
Including all the source code into one main file compiled to one object can work, if the source files cooperate. C can have problems with the namespace, but C++ allows multiple namespaces and you can even put the namespace blocks in the main file around the #includes.
Surely you're joking? Do you actually fail to understand how broken such a behavior would be? Consider the relatively trivial case of a #included file which contained two namespaces, and attempted to reference one another through uses clauses. This is a blatant CFAD behavior.
The source code has to support this, though.
This sentence alone is enough warning that any experienced programmer would be cluebulbing right now.
C can have problems with the namespace
C doesn't offer namespaces.
I've done this on lots of projects and it works great.
Er. Then you had just as many massive coupling problems as the original author appears to. #including source is the wrongest of the novice linking mistakes, and whereas that's a total zealot thing to say, I don't care, because it's also true. You would do well to read up on the basics before giving out any more bad advice.
or an appeal to tradition
Actually, many of the posts have been about how easy it is to introduce errors this way (such as your namespace abomination above.) That said, the reason most of the posts are about speed is because the poster was asking about speed. "Hey, this car magazine talks about cars too much."
Modern compilers will create pre-compiled headers that can include code, usually used for template and inline definitions
Templates aren't code, and inlined code doesn't go into precompiled headers (in fact it cannot, for obvious reasons.)
Actually, even larger projects seem to take longer to link with iostream and windows.h than the source does to compile.
Linking should be virtually instantaneous; it's a question of copying a tiny bit of binary code and setting a pointer at the beginning of a vtbl. If your linking of iostreams or windows.h is taking a visible amount of time, then there's something very wrong with your compiler, or you're making some bizarre mistake which I've never seen (given what you said above, that doesn't seem terribly unlikely.)
Too much inlining will cause code bloat, but the compiler's options should give you control over the balance.
If you had read the standard, then you would know that inline is a hint, not a requirement, much like register. In fact, the language specifies that the programmer cannot control inlining, and that inlining is totally the compiler's decision. If your compiler listens to you, that's because it's being nice, not because you told it to.
Modern compilers also allow you to change the compilation options mid-file.
No they most certainly do not. I suspect you're referring to #pragmas within the source; a real C programmer knows that #pragmas are not C, but rather an explicit backdoor introduced by ISO during standardization. The focus of C and C++ was originally and remains to this day that of portability; #pragma is the work of the devil and should never, ever occur in code. This should begin to give you minor cluebulb about why real programmers are frustrated with MSVC; see also _tmain, main(int, char*, char*), void main(), et cetera.
Any debugger or source analyzer shouldn't have problems handling inline or same-file implementations, or you're using bad tools.
Debuggers and source analyzers do not act on the precompiled source. Rather they act on inserted tokens in the binary, which are given references to source locations. By definition a debugger cannot be affected by inclusion. You seem not to understand many of the tools which you are attempting to discuss.
I bet incremental compilation will make that quite a bit faster.
Chances are he's got massive coupling problems, which can totally throw away any benefit of incremental linking. And by the way, incremental compilation is something totally different; whereas I realize that the error is that of the original speaker, not yours, it should nonetheless be pointed out.
C++ does not support incremental compilation, though ICC and MSVC both have extensions to support it. MSVC refers to it as runtime code generation: you change the source, MSVC swaps out a vtbl, and the new compiled piece of source is literally injected into a running program.
TU seperation is incremental linking, not incremental compilation.
I find it incredibly disappointing that this post, which seems to be the only actually clueful post in the discussion, has had no moderator points applied to it. I would do it myself, except that I'm crusading around splitting hairs.
Please mod parent up: he's not only correct, but absolutely dead-on.
but I don't know of any off-hand.
ccache is probably the least sucking one, but with a good build tree, makefile and strong TU seperation it's virtually never important anyway.
The other problem, of course, is that separating your classes into header and implementation means that if you change the implementation, you only need to recompile that one file and relink, rather than recompiling EVERYTHING
Of course, his original post explicitly deals with this issue, and rejects your opinion out of hand; if you want to tell him about this, you need to help him understand why it isn't working for him. It is frequent for non-mediocre programmers to fail to understand the problems of mediocre programmers; I hereby suggest that you are not stupid enough to grasp the real problem here. It is my guess that he's got major coupling issues, and that any invokation of the compiler ends up revisiting all source files, whether or not they were actually touched.
This can be a matter of a few seconds vs. several minutes.
In the case of Starcraft, it's the difference between 30 seconds and several hours. In the case of Windows NT, it's the difference between a few minutes and two weeks.
If your language supports "static" in the file-scope sense, you could declare every global object as static and reap the compiler optimizations that come with that declaration.
Er. There are no optimizations which apply to globals but not to globals. No, that's not a typo: file-scoped variables are globals whose names are not imported into the external tokenspace. There is no difference from the perspective of the compiler between the two; file-scoping is a purely lexing issue, and lexing cannot optimize in any way.
If your language supports smart inlining, you could end up with code that has been inlined more effectively, since any code could be an inlining candidate regardless of location.
Once upon a time, people were admonished not to use string and vector because various compilers didn't implement them correctly. Then, it was exceptions. Then, it was template specialization. Currently it's exportation and template template arguments (yes, that word should be repeated.)
Programmers which have been around for more than a few years generally hold the same opinion: to hobble your code based on certain compilers being behind is short sighted. Within a year or two all major compilers will be doing the right thing, and in the meantime you've already got the code correctly. Whereas it's a clear case of Ad Verecundiam, I still want to point out that Meyers, Dewhurst, Sutter, Vlissides and Alexandrescu have all spoken strongly against this opinion.
Between then and now, C++ has gained all these capabilities, so there's no reason to think that C++ hasn't gained in the same way.
Actually, the BCPL family has this as far back as B; K&R C had incremental linking from day one, and is a year older than Modula-2 (1978 and 1979, respectively.) That said, the BCPL family did not invent it either; this technique was in practice in Wirth's original Pascal tool in 1970, and may stretch further back than that (in fact, probably does: I'm looking at an early-era Pascal tutorial which lists the benefits of the new language, and that isn't among them despite the huge impact it would have had on machines of the day, suggesting someone else had it first, though I'll be damned if I can think of who.)
I think it's a better and smarter system than separate header and body files, which were a hack to gain this advantage.
Disagreed strongly. Headers have much more far-reaching impacts than Modula's modularization system; consider for example that Modula requires all source to be present at compile time, whereas C only requires that the headers be present. This is a significant design advantage, and as a side effect also allows the distribution of binary libraries, something which Modula-2 does not support. Those two reasons were the basis of inventing headers; they are a better tool, not a hack.
If you have a complicated #include chain, you can wind up with a lot of duplication. Some compilers will complain, some won't.
So sorry: the ODR rule prevents duplicated code from functioning in any compliant C or C++ compiler, all the way back to day one (and in fact into the parent languages B and BCPL.) This is simply false.
However, if you have typedefs, structs or the like, most compliers will complain and not compile your code until the duplications are removed.
If by most you mean all...
so I'll just say that it has been my experience that projects which are developed in a true modular nature are much easier to debug than projects designed in a monolithic nature.
Uh. Modularity and monolithism are not related. Modularity is the design technique of seperated interface definition such that one may swap in alternate implementations of code, such as through TUs, polymorphism, SFINAE or various metaprogramming techniques. One may contrast structured programming, functional programming, lambda calculus or contract substitution.
Monolithism is a development model: designing all at once and then implementing top-down from start to finish. Constrast the waterfall model, iterative development, chain development, and so on.
The time saved in debugging more than makes up for a little time lost in compilation.
Right on: preach it, brother. This is one of the least understood principles of modern design: machine time is significantly inferior to programmer time. Herb Brooks would be proud.
(No, I'm not being sarcastic. Yes, I did just compliment you heavily after criticism.)
If I'm right, you'd probably still want to include header files because you want everything to remain modular. According to software engineering type people, that makes maintenance easier.
Whereas you understand the original petitioner's complaint correctly, including headers in a project which simply mass-merges all the source would have absolutely no effect on modularity. The modularity which developers are probably referring to with regards to headers is the ability to swap out libraries without recompiling client code (though fragile linking in both C and C++ make this a relatively difficult problem without handles or visitors, hence COM and CORBA.)
C keeps symbols local to the module they appear in, so you want to make sure you have naming conventions, namespaces, or some other protection against naming clashes.
C doesn't have namespaces or modules. Locality to a translation unit, which is not the same thing as a module (experience in Clean or Objective C will clear the distinction right up,) is not guaranteed; it is explicitly in the control of the author by way of the "extern" keyword. This is an observation of C's default behavior, not its only behavior; similarly, functions may return things other than int, may accept arguments other than varargs, and may have variables whose construction time isn't auto.
Well, back in the real world, in a properly decoupled project incremental linking is a massive speed win, even when building from the top, as there's far less cross-lexing and as the build tables may be handled a small piece at a time, which is important because their parsing in the compiler itself is generally of O(n^2 log n) time or better. Once you've worked on a large project which fails to make proper decouplings, you will become painfully aware of this trend.
Whereas in this particular project the complete build is apparently faster, that is almost certainly the result of a very naive code tree and/or build scheme; the importance of incremental linking towards speed of compile cannot be overestimated, even in the case of compiling from clean.
2. Much better optimization. Compilers can only optimize within a compilation unit.
This simply isn't true. Whereas only some compilers make cross-TU optimizations, that is not the same as cross-TU optimizations being only able to optimize within a translation unit (why do people keep saying compilation unit? There's no such thing!) Besides, you're dramatically underestimating the commonality of link-time cross-tu counterspecialization, which now exists in ICC, BCC, MSCC, ARM ADS, EDG/Comeau, GHOC, and is in experimental development within GCC.
You're not doing it the way everyone expects you to do it. Certain components (the compiler, the linker, and pre-existing code) might have been designed under the assumption that individual files would be compiled separately.
They most certainly have not been. The C and C++ standards do not allow for such ridiculously inappropriate behavior. Where did you get this idea? Compiler writers may not impose arbitrary restrictions on the codebase in any relation to the local filesystem. This is just untrue.
The pre-existing code might have declared static (per-file) variables or functions in a way that could collide with other code (namespaces might help here).
This is a well known gigantic red flag indicating an amateur programmer. File-scoped variables are antiquated even within the pure C community; the only time they're acceptable in most professional programmer's eyes are within a library which is built alone. In fact, you might want to read the things Kernighan himself said about when file-scoped variables are appropriate in K&R 2; the primary author of the language himself says that this is a fundamentally bad technique and should not be done.
Of course, that you're causing problems by misusing the toolchain and allowing bad code to collide when build trees written seperately are blindly merged without the help of a linker is just not surprising.
The compiler and linker might have limits.
Not if they're standards compliant, they mightn't. Did you know that there's a document out there floating around telling compiler authors in concrete detail what they may and may not do? You should read that before commenting on what a compiler may or may not do; you are simply out in left field, here.
As with every issue you'll ever run into, there are two (or three) sides to it.
Not when you know what you're talking about. Whereas many things are issues of pro/con, many simply aren't; you'll be hard pressed to find pros in the distribution of heavy ordinance to delusional sociopaths, you'll be hard pressed to find pros in setting up a "bring a molester to school day," and you'll be hard pressed to find pros in non-decoupled code, once you've actually read the standard and are aware of the real limitations of compiler authors, instead of your guesses about what might maybe happen if someone wasn't paying attention.
Frankly, if you're using file-scoped variables, you should know better than to #include source. That said, file-scoped variables haven't been a good idea since 1980; even C programmers know better these days.
I think the separate header is simply code duplication and memory limitations of old C compilers.
Headers have never had anything to do with memory limitations, and by definition code in a header cannot be repeated in an implementation file. It's worth noting that code should only ever be in a header if it is to be inlined, which doesn't exist in C until C99, by which point memory limitations simply don't matter. (It is worth pointing out that placing templates in headers is not placing code in headers, but rather placing code factories in headers, which is quite a different thing.)
Larger programs (compilation unit)
The phrase "compilation unit" does not mean anything in C++. I expect you mean Translation Unit, but TUs aren't a function of larger programs; it's simply that in larger programs not seperating your code into TUs is suicide. A translation unit is simply the code block associated with an object file (or other prelinker binary.)
The main problem with headers is that preprocessor stat is global to the entire operation, not per header or C file.
This is not a problem; it is an asset. Without preprocessor effects spanning the compile tree, virtually every metaprogramming technique and virtually every compile-time code removal not built on SFINAE would simply fail.
This makes conditional state flow from one to the other, which makes separate precompiled headers hard (since the conditionals might not match).
State does not exist in the compile tree. This is a critical misunderstanding of the way in which a compiler works - the preprocessor effectively does not exist within the code tree. There is no state whatsoever; that would imply that during either compilation or runtime the preprocessor's effects could be altered, which simply isn't true. There is a good explanation in Modern C++ Design of the limitations which statelessness imposes on metaprogramming techniques in C++, and how one can avoid them by other means such as typelists. As far as making seperate PCH hard, well, it's not really hard in any way. There's no issue of conditionals matching, because since the preprocessor is stateless, there is no way to ordinate a conditional. (There are arguments that metaprogramming techniques like Substitution Failure Is Not An Error defeats this; they are wrong. SFINAE is a compile-time polymorphism, not a conditional; no branch is ever taken.)
Also the header system requires manual making of makefiles (or using quite complex scanners and tools)
Uh, no it doesn't. Here's a rudimentary generic makefile. It would be wise not to discuss limitations of tools with which you are not familiar. Besides, the idea that something like NMake is complex is sort of funny; it's actually quite retarded. Back in reality, it's the languages which implicitly generate header-type interface definitions which need complex scanners, which are built into the compilers themselves; that's probably why you remain unaware of them.
See most Wirthian languages (e.g. Turbo Pascal vs Turbo C++) for examples.
Wirth has nothing to do with Turbo Pascal, which is an instance of (depending on the year) either Borland Pascal or Object Pascal. This is a bit like calling C++ a Kernighan language. Object Pascal and Borland Pascal are both significantly distant from ANSI Pascal, the closest non-dead relative to the original teaching tool.
Namedropping doesn't make you seem correct, y'know.
You don't want to abandon headers; they are the basis of seperation into translation units. If you're compiling from the top and it's faster than compiling small pieces, then that means your source needs a hell of a lot more decoupling; by definition the only way that top-down build could possibly be faster than incremental build is if incremental build is still making everything anyway.
Consider reading Modern C++ Design, specifically the section about generic functors, which should help you with your coupling problem until you have the time to learn to seperate TUs aggressively on your own. In my opinion, the Boost functors are better than the Loki functors, but the book is a hell of a lot better than the Boost documentation, so read it anyway.
Ask a large project supervisor in a company with aggressive compilation tactics about the large compile-time wins of precompiled headers. Whereas it's not the same issue, the same critical foundations are there, and it's generally easier to squeeze out an answer about PCH than properly seperated TUs.
Example games mentioned are often not appropriate (Diablo as an example of a gamne that blends action with roleplaying? Give me a break! Diablo is action and nothing more.)
This is one of those cases where it's difficult to argue the point. As a traditionalist pencil-and-paper gamer, I tend to agree: there is no playing of a role within Diablo. That said, that apellation applies to virtually every game labelled an RPG; Diablo has as much plot as any NES or nearly any SNES RPG. Granted more modern RPGs have a larger sense of scale; that said, that Banjo and Kazooie is huge compared to Mario Brothers makes Mario Brothers no less an action game. There is a plot, which has a concrete beginning and conclusion, involving a specific and unchanging list of non-player characters. There are events which are gone through in a concrete order. There is an apex to the story. You have one of a few roles, each of which has backstory, each of which has their own specially-available quests, there are character promotion decisions to be made, you engage in equipment use and ability use (spellcasting, drap defusing, etc;) whereas it isn't a particularly strong RPG, by video game standards it is indeed an RPG nonetheless.
It just whines back, for dozens of paragraphs
I believe this to be dishonest. It is relatively trivial to show literally dozens of points at which I give concrete examples of my criticisms, as well as justifications thereof and even the occasional refactoring of a point into what I believe to be its more correct form. In order to go beyond what I wrote, given my limited abilities, I'd pretty much have to sketch out a novel. That I am not willing to do.
Good arguments are seldom given
That is a matter of opinion; among the six people which replied, four feel this is a good article and two feel it is not. I find the polarization here surprising: three feel strongly for it and two strongly against; there is only one moderate opinion among the set of six, which categorically unsurprisingly was also the person which bothered to argue against me with examples rather than ad hominem.
I think the point of the IGN article is clear: games leave a lot to be desired.
I did not argue with the intent of the article, only its specific items and execution. I agree with you and your evaluation of the article's intent: it is clear that games have a long way to go. I would like to think that in my occupation I have made small steps in that direction, and hope to make more still, hopefully in larger and more obvious ways than in my past.
The question why that is so, why developers haven't solved the obvious problems in games, is a valid one.
No such question is posed. Indeed, the article poses no question at all: it is simply a laundry list of unrealistic goals and failure to understand or remember mechanisms which have already been invented. Of course, it seems quite curious that on the one hand you accuse me of not justifying my arguments, and on the other decry my use of examples, when my argument is solely that something which exists cannot be invented; how else would you have me address the issue?
"Someone needs to invent a metal box which can be used to transport great weight at great speed with a high quality of user safety under its own power." "Okay, look at Ford, Chevrolet, International, Fiat, Honda, Yugo, BMW." Do you have a more appropriate form of response? If so, I'm ready to learn; if not, I suggest you more carefully consider your own criticisms before forwarding them. (Before you fly off the handle about how I'm treating you, please observe that I am being neither rude nor condescending, something you cannot claim.)
A far better response to his article would be one that explains, in understandable terms, what difficulties must be overcome to solve the problems.
Read the rest of my site. Many such explanations are given. I shall endeavour to link them from wit
I had a whole reply ready, but IMHO it is not worth the trouble replying to.
"Oh, I wrote a reply, but I don't want to paste it because you're not a good person and I don't want to." My eight year old son knows that nobody falls for this sort of passive agressive dismissal; it's disappointing that you do not.
Namedropping doesn't make you seem correct, y'know.
Neither does getting personal about perceived faults, when it's pretty much your own assumptions that are the problem.
Observing that something you've done isn't effective is hardly my getting personal. Believe me, there's no shortage of material; what I said above about my son is, for example, personal, as is the following: turn down the whine knob until you've got something worth saying to say.
So the whole template rant is bogus.
Given that it was not you but the original poster which set the domain of important languages, and given that I also touch on pure-C and pure-Java issues, this protest is as bogus as it pretends what it's attacking to be.
- When I am talking about state, I'm talking about state in the compiler. (which in most _performing_ tools has the preprocessor built in btw)
Yes, I heard you the first time. The reason I referred you to modern c++ design is that you're wrong, and I have neither the patience nor the kindness to explain it to you. Start with section 3.5, or with any page explaining how C++ template metaprogramming is a functional language rather than an imperative language. Before you fly off the handle talking about how you weren't referring to templates *again*, please realize that the observations regarding template MP as a functional language in fact apply to everytihng in the C and C++ preprocessors.
Do not reply until you have read; repeating ignorance is no more argument than repeating falsehoods.
-- by {$i} ({$include in delphi), like #include in C
Actually, you're shooting yourself in the foot here. You're attempting to make the hasty generalization that there are two approaches to bringing outside code into a local place, that C/C++ advocate an "inline header system" (whatever the hell that is) and that Delphi does something different.
What you seem to fail to understand is that uses is a call to the Delphi linker; it is literally the same thing as the linker in C++, and in fact if you take the time to look at borland's BPIL, you'll find that they generate the exact same intermediate language binary. Furthermore, the very same examples you give, {$i} and {{$include}}, are the same as #include. Furthermore, both languages offer still other mechanisms to bring code in or to generate code.
That said, talking about Pascal's differences with C and then discussing what Delphi does is roughly equivalent to describing what Objective C does. Delphi is not pascal any more than Objective C is C. They are distinct languages. Delphi is Borland's third pascal variant, Object Pascal, which follows both Borland Pascal and Token Pascal (the last of which is so old that you pretty much can't find references to it online.)
Please don't lecture to me about Pascal; my use of Pascal predates Borland's very existence.
The main reason, and the fundament of a unit system, why the second way is more optimal than headers, is that the compiler reinitialises before reading a unit interface.
Uh. The pascal unit system is simply an in-code linking mechanism. It's no different than rolling your source together with your makefiles; if you'd bothered to read Wirth's papers on the design of the language you'd find out that Wirth himself suggests that "unit" is nothing important, and pretty much just syntactic sugar.
Now, how the compiler "reinitializes" before reading a unit interface is a little bit beyond me: the unit interface is just what a C++ programmer would call a collection of vtbls. Would you be willing to point me to any point
huhuhuhuhuhu. sorry, it was five in the morning and i'd been talking about sutter a lot. my mistake.
From someone whose URL involves the word "guru," almost certainly without having earned it, to attack a set of reasoned arguments with underpinnings with a nasty comment about appearances is just pathetically self-ignorant.
Wrong. If the compiler sees that a "global" (ie. the outermost scope) variable, a is declared static, then it knows that the value of that variable cannot be changed by other code outside the current translation unit.
A variable which isn't extern already has this facet. There is no change once you introduce file scoping. Don't be silly.
That might enable some optimizations which would not have been possible before (in particular, it may be possible to put the value into a register and keep it there for longer).
Uh. You really think that a variable might be left on the register for a different amount of time based on instructions to the linker? Have you yet realized that if you file-scope a variable and recompile you get the exact same assembly, and that like namespaces, file-scoping has literally no effect on the binary, that it's entirely a language-level safety for the programmer?
Decoupling isn't refactoring.
.
I would say that it is. Mathematically, if you convert (a+b/c)bc into abc+bb and then to b(ac+b), I call it refactoring, since it involves moving factors around.
Neat how you avoided the justification I gave, and instead relied on equivocation to make your argument for you. It doesn't really matter that in mathematics you can push things called factors around; that has nothing to do with Fowlerian refactoring. Should you choose to read his book, you may be surprised to learn that he himself places great emphasis on the distinction between modularization and refactoring.
n programming I say "refactoring" to mean "extract interfaces from your code and use them to pull up modules, which can then be encapsulated into classes".
1) That is an extremely specific case of modularization which happens to also be a case of refactoring; this is a fallacy of composition, wherein you are suggesting that because one extremely limited case is in both sets, then the two sets are equivalent.
2) It doesn't matter how you use the word; that's just not what the word means. I might say "I use the word rape to mean planting flowers on highways;" that doesn't mean that I may then engage in discourse about landscaping with the word rape.
3) Refactoring is a hell of a lot more complex than hand-encapsulation.
It will have a great deal of effect on the problem at hand, which is the increasing the speed of developer builds. If your modules don't let their intrails hang out, changing them does not require rebuilds anywhere outside the module. This is the only real way to solve the problem.
In the case of code with extreme coupling problems, this will actually make things worse. The problem is that coupling is the result of code swatch A requiring knowledge about code swatch B. Encapsulation does not decrease coupling in any way; it simply hides non-coupled parts from one another. Whereas that is certainly a Good Thing (tm), it will not address issues of coupling. Rather, it will force the programmer to bring more interface knowledge to various points within the program, increasing coupling, and thereby deepening the problem wherein code A requires a rebuild of code B to run, even though code B is unchanged. This is why I suggested Alexandrescu-style functors: they have an immediate decoupling effect.
No, this will not have any effect on the problem at hand.
Well, yes; I should have made it more clear that I meant static functions in C, which simply mean that the named function is not used outside the file.
Oh yes, I should have made it clear that I meant a crib mobile which moved itself, not a car, when I said automobile. You should realize that statics are static variables, and that static functions, despite having static in their name, are not statics at all.
The other important use of static is for const arrays inside functions, which should be declared static to allow the compiler to put them in the data segment at compile time instead of building them whenever the function is entered.
Static doesn't have any such effect on UDTs, and on machine types that already happens. This is myth: static local arrays are not in any way faster than nonstatic local arrays. Please try profiling such code in a relatively modern compiler; you should see absolutely no difference at all, like I see with gcc 3.1+, bcc 5.5+ and mscc 7.1
As for threaded code, I never write any
Obviously. You also clearly don't write libraries, where you cannot guess about the nature of outside code. Furthermore, you plainly don't write code which is maintained by others.
event-driven asynchronous designs give much better and faster results
Uh. You do realize that the event mechanism i
Proper logical and physical encapsulation can speed up builds by a huge factor.
True. That doesn't make statics encapsulation at all, let alone proper.
The more extreme encapsulation of pimpl can speed builds even more.
It's always a shame when people talk about sutter's pointer to implementation or fast pointer to implementation idioms without actually understanding them. Pimpl is not encapsulation. Encapsulation is the seperation of implementation from interface, the distinction between "inside" and "outside." Pimpl does not change what is inside or outside; it simply moves the implementation's guts deeper down in order to make operations like copying and pooling faster.
I think he is using 'static' in the sense of 'class variable' or maybe as in 'compilation unit scoped', not in the sense of 'persistent between invocations of a function'
I should hope not: class statics and local/global statics are not distinct in any way, and in context file statics don't make sense.
the third use of 'static' (local statics) is the one that always causes grief when threads enter the picture.
Uh. Consider a singleton flyweight head which has two threads attempting to add to the same weight at the same time. Even though it's a "class variable," there's a race condition.
You could conceivably cause thread problems with class variables, but to my mind that nearly always involves misuse of class variables.
Explain to me please a legitimate use of a class static which does not threaten race conditions absent locking or mutexing. Five points if your explanation does not hinge on a program which is single threaded by nature, making the example utterly useless.
but many of the problems that require massive overhaul can be avoided by the mutable keyword.
No, they can't. Digging yourself deeper into incorrect const behavior is not a solution to earlier const correctness behavior. You would do well to dig around the c2 wiki, where these issues are discussed at great length.
They certainly are not. File scoped variables aren't introduced into the external namespace. Besides, class statics should almost never exist; they are useful for shared buffering, singleton behavior, and little else.
You shouldn't be abusing naming mechanisms, scoping mechanisms or generative mechanisms in order to isolate a variable from the outside world. Make a private member or a singleton instead.
Also, please stop swearing when someone gives you constructive criticism.
Speeding up a full build should not be important.
Agreed.
In the second case, you need some serious refactoring.
Decoupling isn't refactoring. Fowlerian refactoring is about changing the interaction of code, the code paths, and pushing behavior around into conceptually correct normalized areas. Decoupling is just about specifying no more than the bare minimum information required for external linkage in order to reduce cross dependencies.
Encapsulate everything you possibly can. Make stuff private
Whereas these are good principles, it will have zero effect on the problem at hand.
and static.
No. Being static can introduce dozens of subtle bugs, particularly in threaded or reentrant code; unnessecary staticism is probably the single largest source of race conditions in C.
Furthermore, this not only has no effect on compile time, but for primitive types doesn't have the speed increase effect most people seem to expect. Whereas it is true that this prevents a construction and destruction pass during an exit and entry, for primitive types that time is zero in any decent compiler anyway. By contrast, static UDTs are frequent sources of bugs which are generally too subtle for people which make this mistake.
Static is a qualifier with important effects, like volatile, const, export and extern. To apply it in order to squeeze compilation speed is simply incorrect; a static variable is not the same thing as a normal variable in exactly the same way that a volatile or a const is not the same as a normal auto.
Make everything you don't modify const.
Yes and no. Const correctness is a deeper issue than this. Whereas things should be const by default, there are times at which constness is not conceptually correct despite that you won't be altering the variable in this particular use. In those cases do not apply const: not only does it enforce strong and contextually inappropriate strictures on users of the code, but can cause massive maintenance nightmares when you finally get around to using the mutable behavior of said code, and when during the repair of the const correctness mistake you discover that you have to overhaul 20% of your source base dependencies.
Awesome. You've just earned seat five on my friends list in seven years of slashdotting.
Running this way will annoy your compiler, your linker, your debugger, and every other link on your tool chain, and muck up many standard C coding practices.
Conspicuously absent from this list is "other programmers."
Yeah, because clearly designing with a careful eye for scoping and without obvious abuse of inclusion mechanisms is exactly the same as design by contract, and in fact has anything to do with design accretion or preprocessor hacks.
Also, the opposite. </jarvik>
Including all the source code into one main file compiled to one object can work, if the source files cooperate. C can have problems with the namespace, but C++ allows multiple namespaces and you can even put the namespace blocks in the main file around the #includes.
Surely you're joking? Do you actually fail to understand how broken such a behavior would be? Consider the relatively trivial case of a #included file which contained two namespaces, and attempted to reference one another through uses clauses. This is a blatant CFAD behavior.
The source code has to support this, though.
This sentence alone is enough warning that any experienced programmer would be cluebulbing right now.
C can have problems with the namespace
C doesn't offer namespaces.
I've done this on lots of projects and it works great.
Er. Then you had just as many massive coupling problems as the original author appears to. #including source is the wrongest of the novice linking mistakes, and whereas that's a total zealot thing to say, I don't care, because it's also true. You would do well to read up on the basics before giving out any more bad advice.
or an appeal to tradition
Actually, many of the posts have been about how easy it is to introduce errors this way (such as your namespace abomination above.) That said, the reason most of the posts are about speed is because the poster was asking about speed. "Hey, this car magazine talks about cars too much."
Modern compilers will create pre-compiled headers that can include code, usually used for template and inline definitions
Templates aren't code, and inlined code doesn't go into precompiled headers (in fact it cannot, for obvious reasons.)
Actually, even larger projects seem to take longer to link with iostream and windows.h than the source does to compile.
Linking should be virtually instantaneous; it's a question of copying a tiny bit of binary code and setting a pointer at the beginning of a vtbl. If your linking of iostreams or windows.h is taking a visible amount of time, then there's something very wrong with your compiler, or you're making some bizarre mistake which I've never seen (given what you said above, that doesn't seem terribly unlikely.)
Too much inlining will cause code bloat, but the compiler's options should give you control over the balance.
If you had read the standard, then you would know that inline is a hint, not a requirement, much like register. In fact, the language specifies that the programmer cannot control inlining, and that inlining is totally the compiler's decision. If your compiler listens to you, that's because it's being nice, not because you told it to.
Modern compilers also allow you to change the compilation options mid-file.
No they most certainly do not. I suspect you're referring to #pragmas within the source; a real C programmer knows that #pragmas are not C, but rather an explicit backdoor introduced by ISO during standardization. The focus of C and C++ was originally and remains to this day that of portability; #pragma is the work of the devil and should never, ever occur in code. This should begin to give you minor cluebulb about why real programmers are frustrated with MSVC; see also _tmain, main(int, char*, char*), void main(), et cetera.
Any debugger or source analyzer shouldn't have problems handling inline or same-file implementations, or you're using bad tools.
Debuggers and source analyzers do not act on the precompiled source. Rather they act on inserted tokens in the binary, which are given references to source locations. By definition a debugger cannot be affected by inclusion. You seem not to understand many of the tools which you are attempting to discuss.
It can also be easier
I bet incremental compilation will make that quite a bit faster.
Chances are he's got massive coupling problems, which can totally throw away any benefit of incremental linking. And by the way, incremental compilation is something totally different; whereas I realize that the error is that of the original speaker, not yours, it should nonetheless be pointed out.
C++ does not support incremental compilation, though ICC and MSVC both have extensions to support it. MSVC refers to it as runtime code generation: you change the source, MSVC swaps out a vtbl, and the new compiled piece of source is literally injected into a running program.
TU seperation is incremental linking, not incremental compilation.
I find it incredibly disappointing that this post, which seems to be the only actually clueful post in the discussion, has had no moderator points applied to it. I would do it myself, except that I'm crusading around splitting hairs.
Please mod parent up: he's not only correct, but absolutely dead-on.
but I don't know of any off-hand.
ccache is probably the least sucking one, but with a good build tree, makefile and strong TU seperation it's virtually never important anyway.
The other problem, of course, is that separating your classes into header and implementation means that if you change the implementation, you only need to recompile that one file and relink, rather than recompiling EVERYTHING
Of course, his original post explicitly deals with this issue, and rejects your opinion out of hand; if you want to tell him about this, you need to help him understand why it isn't working for him. It is frequent for non-mediocre programmers to fail to understand the problems of mediocre programmers; I hereby suggest that you are not stupid enough to grasp the real problem here. It is my guess that he's got major coupling issues, and that any invokation of the compiler ends up revisiting all source files, whether or not they were actually touched.
This can be a matter of a few seconds vs. several minutes.
In the case of Starcraft, it's the difference between 30 seconds and several hours. In the case of Windows NT, it's the difference between a few minutes and two weeks.
If your language supports "static" in the file-scope sense, you could declare every global object as static and reap the compiler optimizations that come with that declaration.
Er. There are no optimizations which apply to globals but not to globals. No, that's not a typo: file-scoped variables are globals whose names are not imported into the external tokenspace. There is no difference from the perspective of the compiler between the two; file-scoping is a purely lexing issue, and lexing cannot optimize in any way.
If your language supports smart inlining, you could end up with code that has been inlined more effectively, since any code could be an inlining candidate regardless of location.
Once upon a time, people were admonished not to use string and vector because various compilers didn't implement them correctly. Then, it was exceptions. Then, it was template specialization. Currently it's exportation and template template arguments (yes, that word should be repeated.)
Programmers which have been around for more than a few years generally hold the same opinion: to hobble your code based on certain compilers being behind is short sighted. Within a year or two all major compilers will be doing the right thing, and in the meantime you've already got the code correctly. Whereas it's a clear case of Ad Verecundiam, I still want to point out that Meyers, Dewhurst, Sutter, Vlissides and Alexandrescu have all spoken strongly against this opinion.
Between then and now, C++ has gained all these capabilities, so there's no reason to think that C++ hasn't gained in the same way.
Actually, the BCPL family has this as far back as B; K&R C had incremental linking from day one, and is a year older than Modula-2 (1978 and 1979, respectively.) That said, the BCPL family did not invent it either; this technique was in practice in Wirth's original Pascal tool in 1970, and may stretch further back than that (in fact, probably does: I'm looking at an early-era Pascal tutorial which lists the benefits of the new language, and that isn't among them despite the huge impact it would have had on machines of the day, suggesting someone else had it first, though I'll be damned if I can think of who.)
I think it's a better and smarter system than separate header and body files, which were a hack to gain this advantage.
Disagreed strongly. Headers have much more far-reaching impacts than Modula's modularization system; consider for example that Modula requires all source to be present at compile time, whereas C only requires that the headers be present. This is a significant design advantage, and as a side effect also allows the distribution of binary libraries, something which Modula-2 does not support. Those two reasons were the basis of inventing headers; they are a better tool, not a hack.
And modern compiler technology supports it.
Also ancient.
If you have a complicated #include chain, you can wind up with a lot of duplication. Some compilers will complain, some won't.
So sorry: the ODR rule prevents duplicated code from functioning in any compliant C or C++ compiler, all the way back to day one (and in fact into the parent languages B and BCPL.) This is simply false.
However, if you have typedefs, structs or the like, most compliers will complain and not compile your code until the duplications are removed.
If by most you mean all...
so I'll just say that it has been my experience that projects which are developed in a true modular nature are much easier to debug than projects designed in a monolithic nature.
Uh. Modularity and monolithism are not related. Modularity is the design technique of seperated interface definition such that one may swap in alternate implementations of code, such as through TUs, polymorphism, SFINAE or various metaprogramming techniques. One may contrast structured programming, functional programming, lambda calculus or contract substitution.
Monolithism is a development model: designing all at once and then implementing top-down from start to finish. Constrast the waterfall model, iterative development, chain development, and so on.
The time saved in debugging more than makes up for a little time lost in compilation.
Right on: preach it, brother. This is one of the least understood principles of modern design: machine time is significantly inferior to programmer time. Herb Brooks would be proud.
(No, I'm not being sarcastic. Yes, I did just compliment you heavily after criticism.)
If I'm right, you'd probably still want to include header files because you want everything to remain modular. According to software engineering type people, that makes maintenance easier.
Whereas you understand the original petitioner's complaint correctly, including headers in a project which simply mass-merges all the source would have absolutely no effect on modularity. The modularity which developers are probably referring to with regards to headers is the ability to swap out libraries without recompiling client code (though fragile linking in both C and C++ make this a relatively difficult problem without handles or visitors, hence COM and CORBA.)
C keeps symbols local to the module they appear in, so you want to make sure you have naming conventions, namespaces, or some other protection against naming clashes.
C doesn't have namespaces or modules. Locality to a translation unit, which is not the same thing as a module (experience in Clean or Objective C will clear the distinction right up,) is not guaranteed; it is explicitly in the control of the author by way of the "extern" keyword. This is an observation of C's default behavior, not its only behavior; similarly, functions may return things other than int, may accept arguments other than varargs, and may have variables whose construction time isn't auto.
1. Faster compile of the full product.
Well, back in the real world, in a properly decoupled project incremental linking is a massive speed win, even when building from the top, as there's far less cross-lexing and as the build tables may be handled a small piece at a time, which is important because their parsing in the compiler itself is generally of O(n^2 log n) time or better. Once you've worked on a large project which fails to make proper decouplings, you will become painfully aware of this trend.
Whereas in this particular project the complete build is apparently faster, that is almost certainly the result of a very naive code tree and/or build scheme; the importance of incremental linking towards speed of compile cannot be overestimated, even in the case of compiling from clean.
2. Much better optimization. Compilers can only optimize within a compilation unit.
This simply isn't true. Whereas only some compilers make cross-TU optimizations, that is not the same as cross-TU optimizations being only able to optimize within a translation unit (why do people keep saying compilation unit? There's no such thing!) Besides, you're dramatically underestimating the commonality of link-time cross-tu counterspecialization, which now exists in ICC, BCC, MSCC, ARM ADS, EDG/Comeau, GHOC, and is in experimental development within GCC.
You're not doing it the way everyone expects you to do it. Certain components (the compiler, the linker, and pre-existing code) might have been designed under the assumption that individual files would be compiled separately.
They most certainly have not been. The C and C++ standards do not allow for such ridiculously inappropriate behavior. Where did you get this idea? Compiler writers may not impose arbitrary restrictions on the codebase in any relation to the local filesystem. This is just untrue.
The pre-existing code might have declared static (per-file) variables or functions in a way that could collide with other code (namespaces might help here).
This is a well known gigantic red flag indicating an amateur programmer. File-scoped variables are antiquated even within the pure C community; the only time they're acceptable in most professional programmer's eyes are within a library which is built alone. In fact, you might want to read the things Kernighan himself said about when file-scoped variables are appropriate in K&R 2; the primary author of the language himself says that this is a fundamentally bad technique and should not be done.
Of course, that you're causing problems by misusing the toolchain and allowing bad code to collide when build trees written seperately are blindly merged without the help of a linker is just not surprising.
The compiler and linker might have limits.
Not if they're standards compliant, they mightn't. Did you know that there's a document out there floating around telling compiler authors in concrete detail what they may and may not do? You should read that before commenting on what a compiler may or may not do; you are simply out in left field, here.
As with every issue you'll ever run into, there are two (or three) sides to it.
Not when you know what you're talking about. Whereas many things are issues of pro/con, many simply aren't; you'll be hard pressed to find pros in the distribution of heavy ordinance to delusional sociopaths, you'll be hard pressed to find pros in setting up a "bring a molester to school day," and you'll be hard pressed to find pros in non-decoupled code, once you've actually read the standard and are aware of the real limitations of compiler authors, instead of your guesses about what might maybe happen if someone wasn't paying attention.
In real compiled languages, the comments are stripped out in lexing or earlier
Yes, that's the joke.
In C, they're stripped by the preprocessor.
No, they aren't. They're stripped by the tokeniser. There are a number of neat tricks in GotW which rely on this distinction.
Frankly, if you're using file-scoped variables, you should know better than to #include source. That said, file-scoped variables haven't been a good idea since 1980; even C programmers know better these days.
I think the separate header is simply code duplication and memory limitations of old C compilers.
Headers have never had anything to do with memory limitations, and by definition code in a header cannot be repeated in an implementation file. It's worth noting that code should only ever be in a header if it is to be inlined, which doesn't exist in C until C99, by which point memory limitations simply don't matter. (It is worth pointing out that placing templates in headers is not placing code in headers, but rather placing code factories in headers, which is quite a different thing.)
Larger programs (compilation unit)
The phrase "compilation unit" does not mean anything in C++. I expect you mean Translation Unit, but TUs aren't a function of larger programs; it's simply that in larger programs not seperating your code into TUs is suicide. A translation unit is simply the code block associated with an object file (or other prelinker binary.)
The main problem with headers is that preprocessor stat is global to the entire operation, not per header or C file.
This is not a problem; it is an asset. Without preprocessor effects spanning the compile tree, virtually every metaprogramming technique and virtually every compile-time code removal not built on SFINAE would simply fail.
This makes conditional state flow from one to the other, which makes separate precompiled headers hard (since the conditionals might not match).
State does not exist in the compile tree. This is a critical misunderstanding of the way in which a compiler works - the preprocessor effectively does not exist within the code tree. There is no state whatsoever; that would imply that during either compilation or runtime the preprocessor's effects could be altered, which simply isn't true. There is a good explanation in Modern C++ Design of the limitations which statelessness imposes on metaprogramming techniques in C++, and how one can avoid them by other means such as typelists. As far as making seperate PCH hard, well, it's not really hard in any way. There's no issue of conditionals matching, because since the preprocessor is stateless, there is no way to ordinate a conditional. (There are arguments that metaprogramming techniques like Substitution Failure Is Not An Error defeats this; they are wrong. SFINAE is a compile-time polymorphism, not a conditional; no branch is ever taken.)
Also the header system requires manual making of makefiles (or using quite complex scanners and tools)
Uh, no it doesn't. Here's a rudimentary generic makefile. It would be wise not to discuss limitations of tools with which you are not familiar. Besides, the idea that something like NMake is complex is sort of funny; it's actually quite retarded. Back in reality, it's the languages which implicitly generate header-type interface definitions which need complex scanners, which are built into the compilers themselves; that's probably why you remain unaware of them.
See most Wirthian languages (e.g. Turbo Pascal vs Turbo C++) for examples.
Wirth has nothing to do with Turbo Pascal, which is an instance of (depending on the year) either Borland Pascal or Object Pascal. This is a bit like calling C++ a Kernighan language. Object Pascal and Borland Pascal are both significantly distant from ANSI Pascal, the closest non-dead relative to the original teaching tool.
Namedropping doesn't make you seem correct, y'know.
You don't want to abandon headers; they are the basis of seperation into translation units. If you're compiling from the top and it's faster than compiling small pieces, then that means your source needs a hell of a lot more decoupling; by definition the only way that top-down build could possibly be faster than incremental build is if incremental build is still making everything anyway.
Consider reading Modern C++ Design, specifically the section about generic functors, which should help you with your coupling problem until you have the time to learn to seperate TUs aggressively on your own. In my opinion, the Boost functors are better than the Loki functors, but the book is a hell of a lot better than the Boost documentation, so read it anyway.
Ask a large project supervisor in a company with aggressive compilation tactics about the large compile-time wins of precompiled headers. Whereas it's not the same issue, the same critical foundations are there, and it's generally easier to squeeze out an answer about PCH than properly seperated TUs.
Example games mentioned are often not appropriate (Diablo as an example of a gamne that blends action with roleplaying? Give me a break! Diablo is action and nothing more.)
This is one of those cases where it's difficult to argue the point. As a traditionalist pencil-and-paper gamer, I tend to agree: there is no playing of a role within Diablo. That said, that apellation applies to virtually every game labelled an RPG; Diablo has as much plot as any NES or nearly any SNES RPG. Granted more modern RPGs have a larger sense of scale; that said, that Banjo and Kazooie is huge compared to Mario Brothers makes Mario Brothers no less an action game. There is a plot, which has a concrete beginning and conclusion, involving a specific and unchanging list of non-player characters. There are events which are gone through in a concrete order. There is an apex to the story. You have one of a few roles, each of which has backstory, each of which has their own specially-available quests, there are character promotion decisions to be made, you engage in equipment use and ability use (spellcasting, drap defusing, etc;) whereas it isn't a particularly strong RPG, by video game standards it is indeed an RPG nonetheless.
It just whines back, for dozens of paragraphs
I believe this to be dishonest. It is relatively trivial to show literally dozens of points at which I give concrete examples of my criticisms, as well as justifications thereof and even the occasional refactoring of a point into what I believe to be its more correct form. In order to go beyond what I wrote, given my limited abilities, I'd pretty much have to sketch out a novel. That I am not willing to do.
Good arguments are seldom given
That is a matter of opinion; among the six people which replied, four feel this is a good article and two feel it is not. I find the polarization here surprising: three feel strongly for it and two strongly against; there is only one moderate opinion among the set of six, which categorically unsurprisingly was also the person which bothered to argue against me with examples rather than ad hominem.
I think the point of the IGN article is clear: games leave a lot to be desired.
I did not argue with the intent of the article, only its specific items and execution. I agree with you and your evaluation of the article's intent: it is clear that games have a long way to go. I would like to think that in my occupation I have made small steps in that direction, and hope to make more still, hopefully in larger and more obvious ways than in my past.
The question why that is so, why developers haven't solved the obvious problems in games, is a valid one.
No such question is posed. Indeed, the article poses no question at all: it is simply a laundry list of unrealistic goals and failure to understand or remember mechanisms which have already been invented. Of course, it seems quite curious that on the one hand you accuse me of not justifying my arguments, and on the other decry my use of examples, when my argument is solely that something which exists cannot be invented; how else would you have me address the issue?
"Someone needs to invent a metal box which can be used to transport great weight at great speed with a high quality of user safety under its own power." "Okay, look at Ford, Chevrolet, International, Fiat, Honda, Yugo, BMW." Do you have a more appropriate form of response? If so, I'm ready to learn; if not, I suggest you more carefully consider your own criticisms before forwarding them. (Before you fly off the handle about how I'm treating you, please observe that I am being neither rude nor condescending, something you cannot claim.)
A far better response to his article would be one that explains, in understandable terms, what difficulties must be overcome to solve the problems.
Read the rest of my site. Many such explanations are given. I shall endeavour to link them from wit