Slashdot Mirror


Ultra-Stable Software Design in C++?

null_functor asks: "I need to create an ultra-stable, crash-free application in C++. Sadly, the programming language cannot be changed due to reasons of efficiency and availability of core libraries. The application can be naturally divided into several modules, such as GUI, core data structures, a persistent object storage mechanism, a distributed communication module and several core algorithms. Basically, it allows users to crunch a god-awful amount of data over several computing nodes. The application is meant to primarily run on Linux, but should be portable to Windows without much difficulty." While there's more to this, what strategies should a developer take to insure that the resulting program is as crash-free as possible? "I'm thinking of decoupling the modules physically so that, even if one crashes/becomes unstable (say, the distributed communication module encounters a segmentation fault, has a memory leak or a deadlock), the others remain alive, detect the error, and silently re-start the offending 'module'. Sure, there is no guarantee that the bug won't resurface in the module's new incarnation, but (I'm guessing!) it at least reduces the number of absolute system failures.

How can I actually implement such a decoupling? What tools (System V IPC/custom socket-based message-queue system/DCE/CORBA? my knowledge of options is embarrassingly trivial :-( ) would you suggest should be used? Ideally, I'd want the function call abstraction to be available just like in, say, Java RMI.

And while we are at it, are there any software _design patterns_ that specifically tackle the stability issue?"

104 of 690 comments (clear)

  1. You're not the first one.... by DetrimentalFiend · · Score: 4, Insightful

    I'd hate to say it, but you might want to SERIOUSLY consider managed code. You could build some of the parts in C++ if need to be, but doing it purely in C++ seems like a bad idea to me. You're asking for a silver bullet that just doesn't exist...but managed code is getting faster and can be pretty stable.

    1. Re:You're not the first one.... by destuxor · · Score: 2, Informative

      Although I've never had reason to do this myself, I've heard people recommend cross-compiling code onto a PPC or SPARC64 platform and then fuzz your program on those platforms to look for bugs that might not have shown up on x86.
      As for your question about CORBA, look into IceC++. I read about it somewhere and it sounded cool :)

    2. Re:You're not the first one.... by arkanes · · Score: 3, Informative
      I have to agree. You say it can't be changed due to efficency and core library issues, and then list a bunch of components for which there are no core libraries in C++ and which are rarely if ever CPU bound. Further, if you're asking this question then you aren't a highly experienced guru in this field and thats what you need to be to write these sort of applications in C++. Managed Code (tm), as in .NET, is not your only option but you should look at something higher level and more robust than C++. Haskell or one of the other functional languages might be a good idea.

      Test, test, test. Use test driven development if you can. Have a good test harness and use it all the time. Do the stereotypical "random input" tests. Test each and every component to destruction white-box style and then double test all your interfaces. Design by contract can help here. Use a tinderbox that runs continual builds. Maintain strict version controls. Maintain code discipline (getting rid of C++ helps here too). Realize that you probably do not (currently) have the skills to produce the kind of product you are talking about and be willing to commit the time and effort to tear up mistakes, to start over and to teach yourself. What you are attempting to do is not easy.

    3. Re:You're not the first one.... by merlin_jim · · Score: 3, Interesting

      I was going to post pretty much the same thing - managed code approaches C++ efficiency close enough that it shouldn't matter (I've seen figures of 80-95%)

      And, in visual studio .net 2005 there are built in high performance computing primitives - all the management of internode communication and logical data semaphore locking are handled by the runtime - presumably debugged and stable code...

      --
      I am disrespectful to dirt! Can you see that I am serious?!
    4. Re:You're not the first one.... by gadzook33 · · Score: 5, Insightful

      Ah, another true believer. I work heavily in both managed and unmanaged code (c/c++/c#) hybrid solutions. In my experience, a well designed C++ program is as stable as a well designed C# program. Who cares if it "crashes" if it doesn't do what you want? The worst program is one that seems to be working but is generating invalid results. Don't let anyone convince you that C# is going to provide more reliable execution. We use C# for its nice GUIs; C++ for cross-platform portability.

    5. Re:You're not the first one.... by mr_tenor · · Score: 5, Insightful

      WTF? I love Haskell as much as the next programming-language-theory fanboy, but saying "Haskell or one of the other functional languages might be a good idea." in reply to the OP strongly suggests to me that you are just making stuff up and/o are copy/pasting things that you have read elsewhere out of context

      If not, then great! Please post some references to literature which demonstrates how what you've suggested is sane and/or possible :)

    6. Re:You're not the first one.... by Duhavid · · Score: 4, Insightful

      I read his comment as saying that C# would not guarantee
      a good result ( correctness ). And it wont.

      What the guy really needs is a great team and some decent
      process to backstop that team. Not a silver bullet.

      --
      emt 377 emt 4
    7. Re:You're not the first one.... by wft_rtfa · · Score: 2
      I agree. Using some higher level language that allows you to easily call C++ code would be the best choice. I believe C#, Java, python, perl... can call C++ libraries easily. I personally would use Java and C++ if your company/environment is more geared toward Linux/unix or C# and C++ if you can get away with Windows only.

      Building any complex software in unmanaged C++ can get overwelming very quickly and will require more time than Java or C# even if you're a C++ guru. You should only attempt if you are a C++ guru and have lots trusted libraries and techniques at your disposal.

      --
      :-] :0 :-> :-| :->
    8. Re:You're not the first one.... by wft_rtfa · · Score: 2, Insightful
      Don't let anyone convince you that C# is going to provide more reliable execution.

      That is very true. However, if you're not a C++ expert or a C# expert then you will get a more reliable program much quicker in C# simply because it's easier.

      --
      :-] :0 :-> :-| :->
    9. Re:You're not the first one.... by Peter+La+Casse · · Score: 2, Insightful
      I'm not an expert, but I too see lots of people missing the point, so I'll add my two cents. There are lots of comments talking about implementation techniques, and those are very good things to do, but that's implementation, not design.

      Design is about turning analysis models into implementation. Analysis models are the result of analyzing what your system needs to do; they describe what it does, not how it does it. Use some type of formal modeling language (there are several) to exhaustively describe the behavior of your system in a way that is provably consistent. Iteratively refine the formal model until you get enough detail to implement. Use tools (you might have to write some) to prove that your implementation matches the formal design model. When the design is good, use the techniques that others have mentioned to generate a rock solid implementation.

      Writing ultra-stable software is hard. Most choose not to do it. IMO it's less fun than coming up with something in your head and banging out some Python to do it, but it is possible.

    10. Re:You're not the first one.... by gadzook33 · · Score: 3, Insightful
      You made a huge inline claim without really ANY backup!
      mmm...huge inline claim. Sounds like that could lead to memory thrashing. But seriously folks, if you took the time to actually read my post (you know, left to right, top to bottom), you'll see that I never made any such claim. There is simply nothing inherent in C# that will protect you from most classes of errors. Especially the really insidious ones that A.) You don't discover until it's too late and B.) Usually are the result of bad design, not bad language. At the end of the day, C#: Not that different from C++. The rules of logic still apply.
    11. Re:You're not the first one.... by SchroedingersCat · · Score: 5, Insightful

      Here is the clue: if the code *relies* on being *managed* then the design is not stable. Well-designed system will not need a garbage collector, and poorly-designed system will not be saved by the garbage collector.

    12. Re:You're not the first one.... by SEAlex · · Score: 3, Informative

      The use of managed language will not necessarily result in a more stable code. Recovering form SIGSEGV by installing a POSIX handler or detecting the death of the forked child process in C++ can be done with the same ease as catching NullPointer Runtime exception in Java.

      I would agree that having to write memory management code is error prone, but it is possible to be careful (i.e., use auto pointers, stl vectors instead of arays, etc). You do need to be very good with C++, however.

      My suggestion to the author is to look at the application servers that support C++ components. I worked with a small, relatively unknown server, but even that product had a feature that kept the server up even when some C++ component (running as a forked process) crashed.

    13. Re:You're not the first one.... by asmussen · · Score: 4, Funny

      What? You need to use ones AND zeros??? Loser...

      --
      Shawn Asmussen
    14. Re:You're not the first one.... by Nataku564 · · Score: 2, Insightful

      Its not a tinfoil thing, its a stupid people thing.

      Microsoft doesn't want people to say "Interpreted", because that means slow. So they made up their own buzzword which means "better", or something. Its stupid, and all the MS Zealots have latched onto it. The VB.Net guy at work finally spouted it out enough times to annoy me into destroying his perceptions. Eventually it broke down into him getting frustrated and saying "Well what does interpreted really mean anyway, it could mean anything?", to which I said there was a very clear and distinct definition, and that he needs to read more books and less M$.

      Yeah, I know, hotspot compilation down to native code, Java guy rant, .NET guy rant, blah blah blah. In the end, its still interpreted - its just a very very smart optimizing interpreter.

    15. Re:You're not the first one.... by Stocktonian · · Score: 2, Insightful

      Wow, how did this get modded insightful?
      I'm the furthest thing from a Microsoft Zealot, I don't use any MS products and don't recommend them in my consultations, but I still know that Managed != Interpreted. You've mistaken the term "Just In Time compilation" (JIT) for "Managed Code". You even have the nerve to call other people idiots! Mono has an option for performing Ahead of Time (AOT) compiliation to produce native binaries that can still be considered managed because they have been through the verification process. I think the Microsoft equivalent is called Bartok.
      May I suggest you spend a little less time reading anti-MS propaganda and either read some of these books you are so keen to suggest or spend some time actually thinking about what you say, before you say it.

      --
      XePhi Computers sell really cheap Linux CDs! http://www.xephi.co.uk
    16. Re:You're not the first one.... by chiph · · Score: 4, Insightful

      Absolutely.
      What I'm hearing is the guy's boss telling him "And it'd better not crash!"

      Typically, when absolute reliability is needed (nuclear power plants, spacecraft, pacemakers), you start subtracting libraries which aren't known to be absolutely reliable, yet in this case they're adding them. In addition, he's wanting it to run on multiple platforms, which radically increases your testing workload.

      On top of that, he admits he's got no experience in the techniques needed to produce reliable software. Probably has a short deadline, too.

      My crystal ball says he's doomed to failure.

      Chip H.

    17. Re:You're not the first one.... by Anonymous Coward · · Score: 3, Interesting

      Purely functional languages have two big advantages applicable in this case:

      * No (or very, very limited) side-effects. In other words the result of a function is not dependent on the current program state. Once it is exhaustively verified in testing, that function will forever more return the correct results because the run-time state won't affect it.

      * The language itself can often be treated as a specification of correctness, and even formally proved through static analysis. As a trivial example if you write an implementation of factorial in Haskell, it strongly resembles the mathematical definition of factorial -- the code is more of a description of what the correct result is, rather than a set of low-level steps for carrying out the computation as in C.

      Haskell is nice, however I think the original questioner is better off with something like Erlang, which was designed for just this kind of situation. If it's good enough for telephone switches...

    18. Re:You're not the first one.... by j.leidner · · Score: 2, Insightful

      I agree. Haskell is a bad choice _at_present_ at least for the following reasons: (a) hard to find qualified staff to maintain the code, (b) language not standardized and still in flux.

      This is not a comment against Haskell, but against suggesting it as appropriate means, given the poster's situation.

    19. Re:You're not the first one.... by The_Wilschon · · Score: 4, Interesting

      More: http://www.cs.indiana.edu/~jsobel/c455-c511.update d.txt about a guy who wrote the "Fast Multiplication" algorithm very simply in scheme, and then transformed it (using correctness preserving transformations, which are much much easier to do in "Haskell or one of the other functional languages" than in C/C++ and friends) into scheme code that was as optimized as he could come up with, and which furthermore had a pretty much 1-1 correspondence with C statements. He then rewrote it in C (including perfect "goto"s!), and beat all but one person in his class on the speed of the algorithm. Furthermore, he spent significantly less time working on (read debugging) his code than anyone else in the class.

      --
      SIGSEGV caught, terminating

      wait... not that kind of sig.
    20. Re:You're not the first one.... by ultranova · · Score: 2, Informative

      If I kill the java process, does my program still run?

      Of course it doesn't - you just killed it.

      Good, now that we have that bit of understanding down, would you mind telling me where the gcc process is while I am running C++ programs?

      C++ runtime libraries are loaded into the programs memory space. Just like the Java runtime is in the memory space of whatever Java program you are running. The difference is that the operating system has a specific loader for the kind of files (ELF in Linux) C++ compiler produces, and labels them according to this file in the list of running programs instead of labeling them all as ld.so processes; on the other hand, Java runtime contains its own code for loading Java programs, so the system will label them all as Java processes.

      Did this have any point ?

      Yes, the hotspot thing does perform compilation down to native code, and yes that makes it darn fast, but its still entirely reliant on the java process to figure out what to compile, when to compile, etc. For all you know, the thing could decide to compile none of your program down to native.

      And for all you know, gcc could just cat your C++ source files together and add a runtime interpreter to them. If that runtime interpreter was "darn fast", would you know or care ?

      I'm trying to understand your point, I really am, but it seems to be some weird idea of interpreted language being bad even if it is as fast as a precompiled one, and that just doesn't make sense to me.

      Also, did you mean "Java runtime" instead of "Java process" - since the JVM and the program being run form a single process, just like a C++ program and its runtime libraries, so this really doesn't make sense otherwise ?

      If your code runs natively on the hardware by itself (that part is important) then you have a compiled program. If it relies on some other process to read it in and figure stuff out, its interpreted. Compiler books will agree with me.

      C is interpreted, then. On Linux, a C program depends on ld.so to bring all the neccessary C libraries to the process's memory space and link it just prior to execution (or during execution, if you use dynamic lib loading). And, of course, it is the kernel process that reads it to memory in the first place.

      More generally, each and every program depends on some other process to read it in and "figure stuff out", unless you want to suggest that the code will magically jump from the disk to the main memory ? BIOS is propably the only exception to this rule, since it resides in ROM and is in computers main memory at boot. Every other program, including operating system, is loaded by some other program and therefore interpreted by your standards. And of course a modern process will also change machine code execution order to get more execution speed - a clear example of compilation :).

      The real question is: does it matter ? Why on Earth would I care if my code satisfies some completely arbitrary requirement of being "non-interpreted", if the interpreter is so smart that I can't measure a speed difference ?

      --

      Forget magic. Any technology distinguishable from divine power is insufficiently advanced.

    21. Re:You're not the first one.... by synthespian · · Score: 4, Funny

      What, praytell, is the difference between a functional language like Haskell and a well-designed C++ template library?

      Referential transparency.
      That comp.lang.functional thread is interesting because there are guys from Ericsson elaborating on some real-world aspects of referential transparency. As you know, Ericsson uses the funtional programming language Erlang for their switches. See more in: Welcome to a Smarter Way of Programming. Of course, you can't take their use of Erlang seriously, because they're from Sweden, and Sweden, being a fucked-up third-world country with no tech at all, is not an example for America. The mighty AT&T pushed C++, and now the world is better, safer place, where software errors are a thing of the past.

      --
      Main difference between the BSD license and the GPL license: one is from California and the other is from Massachusetts
    22. Re:You're not the first one.... by Stocktonian · · Score: 2, Informative
      So where does it say that managed code can't be native? It is still managed as long as it retains the metadata describing it. I can't stand Microsoft as much as the next Slashdotter but they've not renamed interpreted code. Managed means something very different. The .Net framework isn't a compiler or interpreter, it's a fully fledged virtual machine. The usual way of running that machine is alongside the code you want to execute, doing JIT compilation. An alternative is to create a managed executable running under the .Net framework, with access to libraries and CLR features, but using native code.

      By the way I couldn't care less what you say about "the mantra", I just don't like it when false information gets spread. So until you can find proof that "managed code" running under the CLR requires that it be interpreted, I'm going to stick with the impression you're wrong.

      Sources: The Common Language Infrastructure Annotated Standard [1st edition] pg 8.
      How and when the CIL is compiled to machine code are not specified as part of the standard, and those determinations rest with the implemention of the VES. The most frequently used model is just-in-time (JIT) compilers ... Install-time compilers are another option...
      Figure 1-4 (Execution Model) on the following page clearly shows "unmanaged native code" and "managed native code" existing along side each other. Since this book is basically an expanded version of the ECMA CLR specification the quote and figure are probably available online for you to check but the page numbers are probably different.

      Please cite your sources that show managed code==interpreted and hence satisfy your premise before drawing the conclusion that MS have redefined terminology.
      --
      XePhi Computers sell really cheap Linux CDs! http://www.xephi.co.uk
  2. inline code by jrockway · · Score: 3, Informative

    > Sadly, the programming language cannot be changed due to reasons of efficiency and availability of core libraries.

    You can easily embed C/C++ in other languages. Take a look at Inline::CPP, for example. With code like:


          use Inline CPP;

          print "9 + 16 = ", add(9, 16), "\n";
          print "9 - 16 = ", subtract(9, 16), "\n";

          __END__
          __CPP__

          int add(int x, int y) {
                return x + y;
          }

          int subtract(int x, int y) {
                return x - y;
          }


    you can put the parts that need to be fast in C++, and the parts that need to be easy in Perl. (If you do the GUI in perl, you won't have to worry about portability or memory allocation. And the app will be fast, because the computation logic is written in C++.)

    > The application can be naturally divided into several modules, such as GUI, core data structures, a persistent object storage mechanism, a distributed communication module and several core algorithms.

    Yup. There's no need for the GUI to know how to do computations, remember. The more separate components you have, the more reliable your application (can) be. Make sure you have good specs for communication between components. Ideally, someone will be able to write one component without having the other one to "test" with. For testing, write unit tests that emulate the specs... and make sure your tests are correct!

    --
    My other car is first.
    1. Re:inline code by countach · · Score: 4, Insightful

      Perl? Fuck. He wants a stable app with good code. Sheesh.

    2. Re:inline code by jadavis · · Score: 2, Interesting

      Once I did a hybrid python/C project because C by itself was impossible for me to maintain in the given time constraints.

      It worked amazingly well. There's a little bit of interfacing work that needs to be done, but I found that, in that project at least, the C code didn't need to be modified very often.

      It very often DOES simplify to use two programming languages.

      --
      Social scientists are inspired by theories; scientists are humbled by facts.
    3. Re:inline code by OOGG_THE_CAVEMAN · · Score: 2, Insightful

      The failure mode of circular references defeating reference counting is more general than the toy example.

      It can very easily occur as one builds up complex data structures to make more abstract representations possible, and then using the abstractions in straightforward ways. Imagine embedding a table in an object, used to efficiently find neighboring objects in a network. If that network has any cycle in it, boom. What do you do if the user is the one building the network? Tell them not to use your tool for networks with cycles?

      This is analogous to the problems of manual memory management. In simple cases, i.e., simple enough to see the problem, you can always come up with a "simple" solution: i.e. "caller owns the resulting allocated object, and is responsible for deallocating." The problems develop when the call and ownership chains become complex, and the protocols built on top of protocols eventually fail to cover edge cases. Then, you've got a hell of a mess to crawl through to plug the leak.

    4. Re:inline code by Peaker · · Score: 2, Informative

      Python uses a hybrid approach of refcounting + refloop-finding.

      A language that leaks memory in real use cases is just not good enough. A language that slows down execution for periods of time may be good enough. The difference is that the first impairs correctness, while the second does so to performance. While bad performance can be tolerated, incorrectness can't.

  3. I'm gonna take a guess, but.. by Anonymous Coward · · Score: 5, Funny

    try not to de-reference any NULL pointers and you should be ok..

    1. Re:I'm gonna take a guess, but.. by arivanov · · Score: 5, Insightful
      Well... Someone modded this as funny. Wrong... It is the first comment so far I have seen on this article that comes anywhere near being insightfull.

      The secret of stable system design is designing from failure. Designing and implementing defensively. If you want to design an ultrastable system you start with the failure analysis for every component, following with failure analysis of modules and the entire thing as it grows.

      This in the world of C++ (and C for that matter) quite often means checking paranoiacally everything everywhere for NULLs before doing anything about it.

      Designing and writing from failure means that every system or library call should be assumed to fail first and all failures handled cleanly. This may be quite painfull because it usually requires the development of special tools like wrappers around malloc, file calls, etc that return error conditions which are nearly impossible to achieve on a live system.

      Only after all codepaths for "bad" results have been handled, the actual "normal" codepaths should be written. This unfortunately is not the way code is written in 99% of the shops out there. Most design and implement from success first and add failure handling later.

      Just ask in your shop: "Where is our memalloc wrapper that simulates a failed memory allocation? I need to link versus it to do some testing to see how our app handles NULLs in a few places". The usual answer you will get is "Ugh? WTF you are talking about Dude... We do not smoke that stuff here... Just go and write the code you have been assigned to write..."

      And the results are quite bloody obvious.

      --
      Baker's Law: Misery no longer loves company. Nowadays it insists on it
      http://www.sigsegv.cx/
    2. Re:I'm gonna take a guess, but.. by Anonymous Coward · · Score: 2, Insightful

      I second that one.

      First rule of programming is You never disregard a return value.
      Second rule of programming is You never disregard a return value.

      If it can fail, you check and act on it. Event just to throw an error message and die. Usually, I undo all the things done before in the function. Then, when during testing someone comes back with an error message, the bug or problem (I deal with hardware, so bugs also occur in the FPGAs) can be understood in minutes, with no debugging session involved. For rarely occuring error that's precious. It's often advertised that catching a bug early save money, and you won't catch it earlier than by checking every goddam return value.

    3. Re:I'm gonna take a guess, but.. by iangoldby · · Score: 2, Informative

      What you say is absolutely right, but we must remember that if you are about to try to dereference a NULL pointer, whether you check for NULLness beforehand or not, something has already gone wrong.

      This is why all of the comments about using a managed language are completely missing the point. Catching exception conditions before they terminate the process is great for tracking down bugs and for code that's allowed to recover from errors, but if you need error-free code, then you cannot afford to have these exception conditions in the first place.

      Your other point about checking all return values is 100% right.

      My own recommendation is to keep the code as simple as possible, and avoid anything that 'hides' code. Use OO design if you like, but be very wary of C++ constructs that might result in a whole load of code being fired off behind your back. This is especially true of operator overloading and temporary objects with their constructors.

      Do as little dynamic memory allocation as possible. Sometimes you can arrange things so that you know before you start exactly how much memory you'll need for everything and then allocate this all statically. Even consider accepting arbitrary limitations on data sizes if it helps achieve this and doesn't cost much in terms of flexibility.

  4. Performance? by rjstanford · · Score: 2, Insightful

    If you're willing to compromise performance to the point that you can use CORBA for IPC, then you should be more than willing to write it in the language of your choice, within reason. C, C#, C++, Java, all are far faster than your CORBA transport.

    If you can provide more details about the specific requirements, you might get more informed responses. As it is, though, your stated goals really don't seem to add up.

    Even as stated, I would write the core in a highly tuned fashion (although C++ might not be my best choice for this), then write the GUI in the language of your choice, quite frankly. Optimise the bottlenecks (ie: your core processing) for speed, optimise everything else for maintainability and ease of development.

    --
    You're special forces then? That's great! I just love your olympics!
  5. Development Practices by the+eric+conspiracy · · Score: 3, Insightful

    THere is no silver bullet for what you describe other than sound development practices. The best results in this area are acheived by teams who are constantly refining their processes based on lessons learned in previous software iterations.

    Bulletproof code isn't cheap, but it can be done.

    1. Re:Development Practices by Coryoth · · Score: 3, Informative

      There are no silver bullets no, but there are tools that can help. Splint is a good example of something you can employ to make static checking for buffer overflows, and various dynamic memory errors like misuse of null pointers, dead storage, memory leaks, and dangerous aliasing in C and C++. It doesn't make your code bullet proof, but it can catch a lot of errors that you probably wouldn't otherwise spot. There's a nice paper about what it can do (warning PDF).

      Jedidiah.

    2. Re:Development Practices by swillden · · Score: 3, Informative

      There is no silver bullet for what you describe other than sound development practices.

      True, but it should be pointed out that C++ is well-equipped to make such sound development practices easy. Consider the major sources of instability in C programs:

      1. Buffer overflows. This arises in C because you must allocate and use arrays for buffers. In C++, you should almost never allocate a simple array for use as a buffer in application code. For strings, use std::string. For collections of various sorts, used the STL collections. If you do actually need a buffer for some reason, wrap it in an appropriate class, keep it simple enough that you can easily verify every mode of operation and test the hell out of it.
      2. Dereferencing dangling or NULL pointers. There are several things you should do to avoid them:
        • Use smart pointers. Don't use naked pointers. Use appropriate smart pointer classes.
        • Don't create uninitialized pointers. At the very least, every pointer created should immediately be set to NULL if it can't be set to point at a valid object. Smart pointer classes can make sure everything is automatically NULLed.
        • Check memory allocations. Actually, on systems with virtual memory like Linux or Windows, memory allocations don't fail -- or, rather, if they do, you already have really big problems. It's a good idea to check anyway.
        • Check every pointer for NULL before dereferencing. This one is overkill for most applications, but if you can't ensure that your pointers aren't NULL, check them every time. If you use exceptions, have your smart pointers throw on a NULL dereference, but be warned that exception handling must be designed into your application from the beginning. It's hard to retrofit.
      3. Memory leaks. Other resources can leak as well. To avoid this, you should almost never explicitly free resources. Use "Resource Acquisition Is Initialization", which means that every resource you allocate should be immediately wrapped in an object whose destructor will free that resource (i.e. a smart pointer). If the memory you allocate doesn't have a simple lifetime (which is actually fairly rare, in my experience), use a reference counting smart pointer or similar.
      4. Incorrect library interface use. When you call library code, even if the library itself is solid and well-debugged, you have to use the library APIs as the library authors intended them to be used. Often, that means you have to violate the above recommendations. If the violations are too frequent and egregious, wrap the library in a layer that gives your application a cleaner API. If they're not too bad, just code all of the library calls carefully and review that code thoroughly. Above all, you *must* understand how the library APIs work, especially with respect to memory management and callbacks.
      5. Concurrency. Multi-threaded programming introduces a lot of weird and unexpected failure modes, at least until you really understand it. In general, most application code should avoid concurrency. However, there are some cases where it simplifies the code so much that it is a net win[*]. In those cases, all I can say is: code carefully, and make sure you know what you're doing.

      In my experience, doing the above religiously will ensure you never see segmentation faults. The next step, of course, is to make sure your code correctly implements the desired functionality. C++ is no different from Java or any other OO language in this respect. Clear rquirements definition, modularity, clean separation of concerns and testing, both automated an manual, are the basic keys to generating correct and maintainable code in any language.

      [*] A story: I once asked a guy on my team to write a little program to monitor a bank of modems, accepting incoming calls and exchanging data with the callers. He spent two weeks and produced nearly 10,000 lines of code

      --
      Note to ACs: I usually delete AC replies without reading them. If you want to talk to me, log in.
    3. Re:Development Practices by xyombie · · Score: 2

      Actually, splint doesn't support C++. But there is another product called Flexelint from gimpel.com that does the same thing, but for C/C++. Unfortunetly, Flexelint is not open source.

  6. Here's your best bet. by neo · · Score: 5, Interesting

    1. Write the whole thing in Python.
    2. Once it's bullet-proof, replace each function and object with C++ code.
    3. Profit.

    1. Re:Here's your best bet. by beyonddeath · · Score: 2, Informative

      Wouldn't you want something thats lower level? Im not even sure theres much higher a language than python!. perhaps you need to revisit csc108 (into to comp sci).

    2. Re:Here's your best bet. by YGingras · · Score: 5, Informative

      This is really good advice but it needs more details:

      1) Wrap your legacy libs with SWIG
      2) Code a working prototype in Python
      3) Profile it (never skip this step)
      4) Use SWIG to write the bottle neck parts in C++
      5) Use Valgrind to ensure you are still OK memory wise
      6) Profit!!

    3. Re:Here's your best bet. by Chandon+Seldon · · Score: 2, Informative
      I'll have to back up the start with python plan.

      Two additional points:
      1.) You don't need to replace all the python code.
      2.) Use a garbage collector like http://www.hpl.hp.com/personal/Hans_Boehm/gc/ for your C++ code.

      --
      -- The act of censorship is always worse than whatever is being censored. Always.
  7. They Write the Right Stuff by Pentclass · · Score: 5, Interesting
    1. Re:They Write the Right Stuff by blair1q · · Score: 2, Informative

      The accurate part follows all that bullshit about 9-to-5 droids.

      You can still be a balls-out code-monkey if the verification-analysis-requirements-code loop in your organization is well designed.

      The part about blame is important, too. The fact that gangs of humans are applying a vast store of partially-learned rules to a purely imagined set of requirements through a skein of lossy transmission lines with any number of distractions means that noise is inevitable, and in something as literal as code for Von Neumann machines, any noise means error.

      So it's not your fault, as long as you're honest about it. And if people accept that, you won't try to hide things, and that means the feedback processes will work to iteratively smooth out the kinks until the entire feature space and code set is 100% covered by correct functionality and test.

      Assuming all of the requirements are valid, for which there are only heuristic tools and few guarantees...

    2. Re:They Write the Right Stuff by canuck57 · · Score: 2, Insightful

      Follow NASA's advice... http://www.fastcompany.com/online/06/writestuff.ht ml [fastcompany.com]

      Your post should have been ranked informative +10 and is underrated. Those that think they are "professional" programmers aught to read this and memorize it. This thread has so much BS about what is right for making code stable it just shows how many poorly qualified people there are out there. But for other readers---

      I have been in both kind of shops, dime a dozen out of control cowboy mentality workshops, and a few others with high standards in planing, process, controls and testing procedures. The later produces better software that is not only less flawed and runs better, but costs less in the long term as you don't need an army to program it or to support it's quirks.

      Lets dispell some myths:

      • At minimum, one hour of rational planning and engineering with save 100 hours of programming and 10000 hours of support.
      • Bad code is bad code, it does not mater if it is C, C++, Java, Python, C#, J# or whatever. Adding another langauge will add complexity but solve nothing. If you want to fix it you need a real software engineer and not a programmer.
      • 9 of 10 programmers are incompetant, at best. If they were compared to brick layers, 9 of 10 could not make a straight or vertical wall. Same goes for software managers. This is the real reason of outsourcing.
      • Those that do know how to code either work alone or in qualified shops with diciplined processses. The later being the best.
      • Most I/T shops have the incorrect attitude, "we are not going to kill people by rushing this, so quicly do it". The truth is they don't know how and are incapable of doing it right the first time and have come to accept the high maintenance costs associated with sloppy computing practices. As their user perception deteriorates, another reason to outsource.
      • Ever notice how I/T vendors bypass techs? Don't trust those who do. If you have a wizard on staff that is socialble, use them but don't abuse them or they will leave. If they are anti-social, loos them.
      • Management is shallow in their vision, look at the long term in team development and raising the bar of the quality of software products. There will be less to worry about when you sleep.
      • The act of making the programming changes should be mechanical, repeatable and quick if planning and engineering are functioning correctly. If your programmers need spend more than 8 hours overtime per month your engineering and planning needs development. Also, if you have more programmers than engineers your resource base is out of whack.
      • Want to manage your project for success? Work the process not the technology.
    3. Re:They Write the Right Stuff by Anonymous Coward · · Score: 2, Insightful

      You have it correct. I worked in the GN&C FSW team this article describes from 1989 to 1994. I wrote code under an ever changing and improving process with a team of excellent managers, requirements analysts, peer developers, and Level1/2 testers. Generally, we worked 8 hours a day together. Overtime **was** the exception. Generally, our families got together outside work at least one night a week for softball, basketball, vollyball, or "off site team building" .

      In the real world - I work in telecom now - the idea of what a "bug" is, is completely different than what the GN&C guys thought a bug was.

      I've worked in places where everyone is fanatical about quality and even a memory leak found in your code what considered a failure to a place where accelerator keys where never tested because the gui coder and tester only used mice. The accel keys worked as a fluke, not as a rule. That code crashed constantly, except on the GUI programmers' machine. He always said, "it works on my machine." Like that is useful to a customer.

      High quality and productivity standards, repeatable processes and schedules, and "being careful what you measure, because that is what you'll get" is what I learned from my GN&C experience. At the time, I didn't realize it, but that was probably the best job I've ever had. Except for the paycheck, that is.

  8. Re:Get another programmer by Philip+K+Dickhead · · Score: 5, Funny

    Make sure his name is something like "Bjarne" or "Knuth".

    --
    "Speaking the Truth in times of universal deceit is a revolutionary act." -- George Orwell
  9. Don't get too fancy... by Pyromage · · Score: 4, Informative

    First, consider how complex you want to make the system. The decoupling is a good idea, I think. However, I don't think that having modules automatically restart one another is a good idea; it introduces a whole slew of other problems. At most I'd say use a watchdog process (principle of single responsibility).

    Furthermore, you're crunching large amounts of data, so I'm guessing batch processing. If you can have the application not be a server, then you simplify things a lot. Make it a utility that takes data on standard input and runs whatever analysis you need, and duct tape it together with cron or a simple program that watches for new input files.

    Also, I'd like to suggest that you consider whether other languages could be efficient for the task. For example, Java is pretty good numerically, and as far as your libraries go, see if you can use SWIG to generate JNI wrappers. Also, then you get Java RMI.

    Next, get them down to one platform. It's *way* easier to develop software with tight constraints on a single platform (versus multiple platforms). Investigate QNX: a reliable operating system (though admittedly quirky) with a beautiful IPC API. In any case, make sure you get a well-tested library with message queues, etc. You don't want to be using raw sockets; you could but that's just another pain in the ass on top of everything else.

    Last, figure out what the cost of a failure is. Getting that last few percent of reliability is very very expensive. Unless you're a pacemaker or respirator, the cost of failure is probably not as high as the cost of five nines of uptime.

  10. Don't code to impress. by jellomizer · · Score: 5, Informative


    When coding something that needs to be stable, you need to keep your ego aside and concentrate on the task at hand. Stick with tried and true methods don't go with any algorithm that you are not 100% comfortable with even if it makes the code less ugly. Be sure to follow good practices make many function/methods, and make each one as simple as possible, makes it easier to check each function for bugs when they are simple. Secondly document it like you never want to touch the code again (in code and out of code), you want to know what is going on at all time and the bigger it gets the larger chance you could get lost in your own code. When working in a team and you are in someone else's code document that you did the change.

    Next take into account what causes most Crashes.
    Bad/Overflow memory allocation.
    Memory leaks.
    Endless loops.
    Bad calls to the hardware.
    Bad calls to the OS.
    Deadlock

    If you are going to decouple modules keep in mind that you will need to do as much processing as possible with minimum message passing and allow for mirrors so if one system is down and other can take its place, without killing the network.

    For IPC I tend to like TCP/IP Client server. But that is because it tends to offer a common platform independence and allows for expansion across the network. Or try other Server Methods such as a good SQL server Where you can put all the shared data in one spot and get it back. But not knowing the actual requirements it may just be a stupid idea.

    I would suggest that you also ask in other places other then Slashdot. While there are many experts on this topic there are also equal if not greater amount of kids on there who think they know what they are talking about, or they have there ego in this technology/or method.

    --
    If something is so important that you feel the need to post it on the internet... It probably isn't that important.
  11. Use state machines by Warlock48 · · Score: 2, Informative

    State machines help make sure you cover (almost) all possibles cases your app may encounter.

    Here's a great framework to start with:
    http://www.quantum-leaps.com/products/qf.htm
    And the book:
    http://www.quantum-leaps.com/writings/book.htm

  12. Fault Tolerance Vs. Stability by aeroz3 · · Score: 5, Insightful

    I think perhaps what you REALLY mean here by stability is Fault Tolerance. It's impossible to write code that has zero defects, outside of any trivial examples. Real Code Has Real Defects. Now, as you talk about modular design and being able to restart modules, you're talking about, not stability, but fault tolerance; the ability of the application to recognize and recover from faults. For instance, you can't necessarily guarantee that the module on machine A running task B won't die, hell the computer could accidently fry, but if your application was Fault Tolerant then the application would kick off another process somewhere else on computer C to rerun job B. Stable systems aren't built necessarily by trying to write defect-free code, but by recognizing that defects will occur and architecting the system in such a way that it can recover from them. Here you need to be concerned about things like transactions, data roll-back, consistency, techniques (active vs. passive, warm vs. cold). The key thing is before you even write a LINE of this C++ code, make sure that you have a complete, comprehensive ARCHITECTURE for your application that will gracefully handle faults.

    1. Re:Fault Tolerance Vs. Stability by GileadGreene · · Score: 2, Informative

      Wow. An entire thread devoted to this question, and so far this is the only answer that actually addresses the problem. Every other suggestions seems to be "changes languages", or "here's how to avoid bugs".

      Anyway, let's talk specifics here. For the theoretical end of software fault tolerance, you can get a quick overview here or here.

      In terms of practicalities, I know of an older fault tolerance library for Unix that includes watchdog, checkpointing, and replication utilities, and was created by AT&T (details and downloads here). A newer version appears to be available for Windows under the name SwiFT. Disclaimer: I haven't actually used either of these, and they both seem a little old. But I don't know of anything newer that isn't a proprietary in-house solution.

      Finally, in terms of general design patterns for fault-tolerant distributed systems, you can't go past Joe Armstrong's PhD thesis, "Making reliable systems in the presence of software errors". While it's mostly about Erlang, many of the ideas he presents are readily applicable to other languages too.

  13. be assertive by Anonymous Coward · · Score: 2, Informative

    be assertive

  14. Pretend you are writing code for an airplane... by smackenzie · · Score: 2, Informative

    ...that you are about to board.

    I've spent over a decade refining how best to create stable, great software. And guess what? I still learn things every day. If you are really new to enterprise-grade software, the best thing you can do is search amazon and choose 3 to 5 great books about writing stable, bug-free enterprise code and just start reading and scheming. Give yourself lots of time. Be neurotic, type-A, attention to every detail, stay up at night wondering how your system could fail and what you can do to prevent it. Some immediate thoughts, however:

    1. Good hardware. Obviously. Redundancy everything, self-diagnosing, etc. How can things go wrong? What will go wrong? How can I know when something is going wrong? How I can fix it quickly without impacting the system? Etc.

    2. Enterprise grade (n-tier) architecture: You'll definitely want to do something where you have a database running on one or two (or more) machines, at least two business servers and at least two web servers. Redundancy is good. As you suggested, a setup like this lets you isolate problems (and provides for better security in general).

    3. Test, test, test. From the very start, every day to the very end. Start coding by writing test suites for your code. Learn about unit testing, black box testing, user testing, regression testing, etc. And hire developers and QA whose sole job is test, test, test using great automated testing software.

    4. Profile. Stress-load-test. Know how your system responds to all scenarios. Feel comfortable knowing the limits of your system. There should be no surprises.

    5. Assert. Learn the magic of assert(). If your code isn't at least 25% asserts, you are not trying hard enough.

    I told myself I was only going to write the first five thoughts that came to my mind, otherwise I could spend weeks trying to answer your question!

  15. It's simple, really by eclectro · · Score: 3, Funny


    Use TPS reports. You'll thank me later.

    --
    Take the cheese to sickbay, the doctor should see it as soon as possible - B'Elanna Torres, "Learning Curve"
  16. +5 Funny by amling · · Score: 2, Funny

    I wish I could mod the article +5 Funny.

    --
    70e808a22cb027cde4a6abddf6435d55
  17. C++, automatics without limits will destroy you by jimmydevice · · Score: 2, Insightful

    If your develop safety critical code, or anything that requires hi-rel you need to break down the application into functional testable units, with test fixtures to test each module. Then a integration test framework. You can't create a "verified" correct system with ad-hoc testing. Unless you're very good and you own the whole thing and then it's just you that knows it's right, Ya right.
    JimD.

  18. Good call by lifeisgreat · · Score: 2, Interesting
    Good call. I'm not sure why C++ is being mandated for something that has stability as a top priority. Though there are some language-independent things that should be taken into account:

    Executive summary of this post: Keep it simple. As simple as it can be while getting the job done. The more buzzwords you think about implementing, the more you need to reconsider whether you really need that whiz-bang feature.

    You need to abstract your design into really independent layers, such that the backend processing can be done across linux, windows and even beos slaves simultaneously, and the frontend is viewable via a web interface, fed into excel or whatever. You can't look at this as one big project, but many independent (and more easily verifiable!!) applications cooperating with each other.

    My impression from the description is that you want a system like folding@home for corporate customers - they have a whole heap of data they want analyzed (parallel workload across many clients) and a small subset of results they're interested in. Don't make things any more complicated than they have to be - the data sets could simply be files that are partitioned by a master, sent out when requested to client workhorse computers, getting there by http, nfs or whatever, processed, and the results returned into an incoming directory for a simple frontend to tabulate.

    The biggest mistake you could make is having one gargantuan application in charge of everything. The race conditions will drive you mad, be they in data access, allocation, retrieval, dispatch or anything else you're trying to manage that the OS could do for you.

    Just look at Froogle. Their millions upon millions of store/price listings are fed by people ftp'ing a feed of tab-separated text values.

  19. Re:XP by Anonymous+Brave+Guy · · Score: 3, Insightful

    Extreme programming is your worst enemy on this one. If you need a system that is truly reliable, you cannot take an approach that fundamentally bases its quality controls on a finite number of tests, unless you can test absolutely every possible set of inputs your program can ever receive (legitimately or otherwise).

    Testing is good, of course, but for this sort of job, you must have a proper design, such that all components can be properly verified. (And of course, you must have a proper spec against which to verify.) The XP methodolgy is pretty much the antithesis of what's needed here.

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
  20. Re:XP by Fulg · · Score: 2, Informative

    Extreme programming is your friend on this one. Doesn't matter what language you use, test and retest at every change. Testing is the only, only, only way to get extremely stable software outside of formal verification methods.

    Exactly. Three words: Test Driven Development.

    Since you're tied to C++, may I suggest CppUnitLite2 1.1...

    It's incredible how much more productive you can be writing the tests first (contrary to what you might think initially). I hardly ever need a debugger anymore, and I know that the code I wrote does the right thing, and doesn't adversely affect something else.

    Put the unit tests as a post-build step (or a dummy target in a makefile) and any defect will pop up instantly. If you find a bug not covered by your test suite, add a test that reproduces the problem, ensuring that it will never bite you again.

    If you're not familiar with TDD, check out Wikipedia for an explanation and some useful external links: http://en.wikipedia.org/wiki/Test_driven_developme nt

    --
    gcc: no input sig
  21. test with valgrind! by graveyhead · · Score: 4, Interesting

    valgrind -v ./myapp [args]

    It gives you massive amounts of great information about the memory usage of your program.

    The other day I spent nearly 3 hours trying to decode what was happening from walking the backtrace in gdb. Couldn't for the life of me figure out what was happening. Valgrind figured out the problem on the first run and after that, I had a solution in a few minutes.

    Highly recommended software, and installed by default on several distributions, AFAIK.

    Enjoy!

    --
    std::disclaimer<std::legalese> sig=new std::disclaimer; sig->dump(); delete sig;
  22. What a frightening post. by AltGrendel · · Score: 2, Insightful

    Sounds like the poor soul is in over his/her eyeballs.

    --
    The simple truth is that interstellar distances will not fit into the human imagination

    - Douglas Adams

  23. Forget it. by Pig+Hogger · · Score: 4, Funny
    Forget it, with C and C++.

    Those are low-level programming-jock languages disguised as high-level languages. As long as the punks who program them will have pissing contests in code obfuscation, you can count on having buffer overflows and memory leaks.

    1. Re:Forget it. by yamla · · Score: 2, Interesting

      There's no excuse for buffer overflows and memory leaks in C++, not with TR1's smart pointers and not with the standard library's containers. That's not even considering garbage collectors which have been available in C++ for years.

      --

      Oceania has always been at war with Eastasia.
  24. Unit Testing and Smart Pointers by pjkundert · · Score: 4, Insightful
    60,000+ lines of communications protocol and remote industrial control and telemetry code. No memory leaks, and less than 5 defects installed into production.

    The reasons? A unit test suite that implements several million test cases (mostly pseudo-random probes -- the actual test code is about 1/3 the size of the functional code). In fact, the "defects" that hit production were more "oversights"; stuff that didn't get accounted for and hence didn't get implemented.

    Just as importantly; every dynamically allocated object just got assigned to a "smart pointer" (see Boost's boost::shared_ptr implementation).

    Quite frankly, compared to any Java implementation I've seen, I can't say that "Garbage Collection" would give me anything I didn't get from smart pointers -- and I had sub-millisecond determinism, and objects that destructed precisely when the last reference to them was discarded. The only drawback: loops of self-referencing objects, which are very simple to avoid, and dead trivial if you use Boost's Weak Pointer implementation.

    We didn't have access to Boost (which I Highly Recommend using, instead of our reference counted pointer) when we first started the project, so we implemented our own Smart Pointers and Unit Testing frameworks.

    I've since worked on "Traditional" C++ applications, and it is literally "night and day" different; trying to do raw dynamic memory allocation without reference counting smart pointers is just insane (for anything beyond the most trivial algorithm). And developing with Unit Testing feels like being beaten with a bat, with a sack tied around your head...

    --
    -- -pjk Perry Kundert perry@kundert.ca http://kundert.2y.net
  25. Congratulations! Nice Work! by aendeuryu · · Score: 5, Funny

    "I need to create an ultra-stable, crash-free application in C++. Sadly, the programming language cannot be changed...

    From zero to flame war in under 20 words. Well done!

  26. Agreed. and a few more thoughts. by jd · · Score: 4, Insightful
    • Where they exist, use fault-tolerent components for interconnects. Making things fault-tolerent is tough, so re-using such stuff will simplify the task. Best of all, use stuff with a significant history behind it, because communication will be the biggest headache and bugs there will be hard to pinpoint exactly.
    • When coding, assume that anything can crash. I don't care if you use exception handling, reactive methods or a purple pizza, but you want components to be able to recover from failure (by restarting if need be) and you want anything that talks to it (and the data!) to be able to survive a loss of connection and handle the condition in a predictable way. (This may mean resending to another node, waiting for the old one to reset, buying said pizza over the Internet, whatever.)
    • Keep It Simple! The more layers, the greater the liklihood of bugs. (There are exceptions - if you're using CORBA, then the ACE ORB is heavyweight but generally considered pretty solid. That's partly because it has a decent amount of maintenance and has been around a while. I would probably not go for lesser-known ORBs, though.) The more complexity you can avoid, the more certainty you can have that the code is solid.
    • Analyze, Specify, Design, Implement, Validate. There are no "perfect" techniques to Software Engineering, but a few things generally hold up fairly well. The first of these is to keep the steps in the process as clean and methodical as practical. There will be some overlap, but in general you can't implement good code until you know what good code you want implemented.
    • Testing Is Important. There are more schools of thought on testing than there are programmers. (At last count, at least three times as many.) Even if nobody is quite sure what role testing has, most seem fairly convinced it has got a role. One popular creed states that design should be from the top down and testing from the bottom up. (ie: test at the level of the components that call nothing else, then build up step by step.) Another states that since you have a specification (you do, don't you? :), you can write the tests according to the specification first, then write the code to comply with the tests. You can even follow both approaches, if it helps you feel better. Just pick something and stick to it. My preferred testing method is to check "typical" conditions, boundary (extreme) conditions and erronious conditions.
    • Never assume that some other coder's assumptions about the compiler's assumptions of what was assumed by someone else entirely bears any resemblance to what you think. Computers know all about luck and hope and how to utterly crush them when you're not looking.

    Yes, some of those do conflict. How to keep things simple AND have fault-tolerence, for example. That's where a good design comes in handy, because you can get a better feel for where you should make the trade-off between certainty of working, certainty of working later on and getting some sleep this side of 2008. It's all a matter of weighing the options and investing time in the place most likely to benefit.

    (Because everything is a trade-off, anything listed above may not apply. But then, it may not need to. If you've tested a component thoroughly along all boundaries, a good sample of valid conditions and a good sample of erronious conditions, AND everything has been kept as simple as possible so that really wierd cases are unlikely to crop up, then you may decide you can simplify or eliminate fault-tolerent components. There is no point in catching errors that won't occur. In fact, that adds complexity and violates the Keep It Simple rule.)

    Oh, and as this is a networked system, testing should include testing network I/O. Use packet generators if necessary, to see how the system handles erronious packets or massive packet floods. You don't want "perfect" responses (unless you can define what "perfect" means), you want reliable responses. If X occur

    --
    It's a small world and it smells funny; I'd buy another if it wasn't for the money; Take back what I paid (SoM)
  27. my experience by larry+bagina · · Score: 3, Informative
    I'd say try to have lots of little programs that do one specific thing -- easier to test and verify. Also, if (when) a bug is found it should be easier to fix, and hopefully will have less impact than if it was a monolithic application.

    I've dealt with software that automatically restarts a dead process, and in my experience, it doesn't work so good. If you want ultra-stable software, you want to know what caused the crash and why.

    For your situation, where I guess you're doing lots of time consuming computing, I'd think you should also set checkpoints, save intermediate results, or something, so if it does crash, you can restart in the middle instead of going back to 0. (A standard practice when I was analyzing large databases for corruption, a task that could take days)

    --
    Do you even lift?

    These aren't the 'roids you're looking for.

  28. Listen to what he said!! by logicnazi · · Score: 5, Insightful

    Jesus christ people he is asking you how he should go about building an ultra-stable application in C++. He told you he *has* to build it in C++ because there are critical libraries and other components that aren't availible in C++. Telling him he shouldn't build it in C++ anyway just isn't helpfull.

    I hate to break it to people but there *are* libraries, especially for types of scientific computing, that are only (reasonably) availible in C++ or sometimes FORTRAN. Not only would abandoning these libraries mean he would completely have to reinvent the wheel but also might cause serious compatibility problems not to mention a much greater ongoing maintenence responsibility (he can't just check his program to make sure things still work when someone fixes a library bug).

    Moreover, the idea that because he is considering using CORBA, IPC or whatever else speed can't matter enough to require C/C++ is dead wrong. It is true that whatever *parts* of the process are done using these components may not require huge amounts of speed but this doesn't mean one of these components isn't doing something very processor heavy.

    In particular what he says sounds like the situation in some areas of scientific computing. If one is writing a program to do some sort of simulation or similar math intensive operations speed can be *very* important in the critical parts of the code but (in some cases) transfering information to the GUI or other components need not be particularly speedy (increasing by an order of magnitude may make a small difference in overall runtime). Imagine a program that does some kind of weather, or nuclear detonation simulation. The cross-processor communication and the core simulation kernel need to be very fast but the GUI and data input components need not be particularly fast. Also it is my understanding that often the critical libraries in this area are often only availible (at least freely) with C/C++ or fortran bindings.

    Anyway I think it is important to distingush several different goals, ultra-stability, minimal downtime, and minimal data/computation loss. For instance a climate simulation that may run on a supercomputer for months it is very important to have minimal data/computation loss (i.e. if something goes bad you don't lose months of very valuable supercomputer time) but you need not have ulta-stability or minimal downtime. As long as when any node crashes the simulation can easily be restarted without loss of data there is no problem. On the other hand if you are running a website like slashdot it is minimal downtime that is important it doesn't really matter if some of the web server processes are rebooted once in awhile. If, on the other hand, you are writing code to monitor a nuclear power plant it is ultra-stability that is important (though I can't at the moment think of something that requires distributed processing and ultra-stability but I'm probably just missing something).

    So I think the answer depends on what sort of stability you want. If it is important that no individual *node* crashes (though the GUI/other non-core components can crash) then you should pursue the seperation you described above. I have to admit I'm not an expert here but the client-server model (like mysql, X etc.) seems to work well in this context. However, this depends alot on what sort of data you need to transfer. If you just need to send the core setup commands and get back mostly unstructured info (say a grid of tempratures or other simple datasets) then I would suggest sticking with one of the simpler abstractions and don't get lost in CORBA. On the other hand if you need to send back and forth real objects with significant structure then creating your own serialization system/bindings is just asking for bugs.

    On the other hand if what you want is minimal data/computation loss, downtime, or any other property where it is the overall system you care about not a crash at any particular node then I suggest concentrating less on dividing any one node into comp

    --

    If you liked this thought maybe you would find my blog nice too:

    1. Re:Listen to what he said!! by Lumpy · · Score: 2, Insightful

      Jesus christ people he is asking you how he should go about building an ultra-stable application in C++. He told you he *has* to build it in C++ because there are critical libraries and other components that aren't availible in C++. Telling him he shouldn't build it in C++ anyway just isn't helpfull.

      And what he is asking can not be done BECAUSE of the libraries.

      you CAN NOT guarentee that the libraries are 100% stable. Typically I find that stability points to the libraries and you have to write your own to get your high stability.

      Granted this is from an embedded systems point of view. but if he is looking for ultra stable (5 nines) then he has to change things. Depending on XYZ library means that your stability can only be as stable as THAT library and some of them out there really suck at stability.

      --
      Do not look at laser with remaining good eye.
    2. Re:Listen to what he said!! by GileadGreene · · Score: 3, Informative

      So what he needs to do is develop a design that is robust in the face of errors. In other words, it needs to be fault tolerant. There are well-known design practices for doing this (checkpoints, watchdogs, rollbacks, etc.) as well as design patterns for robust distributed computation (see, for one example, Joe Armstrong's thesis on making reliable systems in the presence of software errors.

      No, the situation the OP is in is not ideal. But it's also not impossible to work with, and there are techniques that can help him to get closer to achieving his goals within the constraints placed upon him.

  29. Don't reinvent the wheel by DigitalCrackPipe · · Score: 4, Insightful

    Avoid the latest "big thing" for the core of your project. It's usually specialized, non-portable, etc. The standard template library for C++ (for example) is here to stay, with tested algorithms that are safer and faster than you can usually write (because they are optimized for the platform you compile on). For the GUI, on the other hand, you may be better off with a GUI-based language/tool. That's less likely to be portable, but that's the way GUIs work.

    Next, spend some time upfront on your design, with things like use cases, sequence diagrams, and other visualization tools to help you understand just what you want to happen in best case situations as well as failures. The level of detail/formality required is a moving target, so update as needed. You should have a solid error detection/correction plan so that you can design each component to follow it. Also design for test and with logging - it will help you while debugging, while testing, and while fixing the bug the customer is seeing.

    Make sure management will allow sufficient time for testing. A lot more lip service goes into support for testing than actual schedule and money. Your test plan should be as bulletproof as your design.

    That's my 2 cents. And a random book recommendation: books like Scott Meyers' "Effective " provide info on effective/error reducing ways to use the language/libraries, but won't help you get started with the architecture.

  30. robust software by avitzur · · Score: 4, Interesting

    Way back in 1993, thanks to a three month schedule delay in shipping the original Apple Power PC hardware, Graphing Calculator 1.0 had the luxury of four months of QA, during which a colleague and I added no features and did an exhaustive code review. Combine that with being the only substantial PowerPC native application, so everyone with prototype hardware played with it a lot, resulted in that product having a more thorough QA than anything I had ever worked on before or since. It also helped that we started with a mature ten year old code base which had been heavily tested while shipping for years. Combine that with a complete lack of any management or marketing pressure on features, allowed us to focus solely on stability for months.

    As a result, for ten years Apple technical support would tell customers experiencing unexplained system problems to run the Graphing Calculator Demo mode overnight, and if it crashed, they classified that as a *hardware* failure. I like to think of that as the theoretical limit of software robustness.

    Sadly, it was a unique and irreproducible combination of circumstance which allowed so much effort to be focused on quality. Releases after 1.0 were not nearly so robust.

  31. Re:Yeah, c++ hasn't been successfully used before. by dezert_fox · · Score: 2, Insightful

    He didn't complain about anything... you added that part. He asked for advice. Were more people to do so, instead of relying on their overconfidence (like you) to get jobs done, maybe we wouldn't have computer instability be such an issue.

  32. And Remember to Design a Testable System by david_costanzo · · Score: 2

    I completely agree with the parent, but I want to add a few things.

    Before you do your design, you should try understand all of the system's requirements. Just how much reliability is really needed? Are lives at stake? Is a lot of money at stake? Is a system failure just inconvenient? Remember that each extra "9" in reliability multiplies the cost of the project by 10, so make sure you understand just how reliable your system must be before you start.

    Once you understand the system's requirements, make sure that your fault-tolerant design is testable. Then design your tests (before you write a single line of code). Don't make the mistake of leaving testing until after implementation. Make sure all of your interfaces are specified and that the subsystems are decoupled enough such that you can test each unit individually and thoroughly. What happens if a subsystem receives bad input? What happens if a subsystem takes longer than expected to respond? How long is too long? What happens if a subsystem returns bad output? You should design your system such that you can replace any subsystem with a misbehaving (test) subsystem and that your overall system responds appropriately. In the process of designing your test, you should expect to find many design defects (or design glosses).

    You should also get someone to peer-review your design (and your tests) with the mindset of making your system fail. People with different backgrounds will have different experiences of what can go wrong; don't expect that you have thought of everything yourself.

    By now you may be thinking "that's a lot of extra work". You're right, it is. But it's all necessary. You can scale back some of this depending on how much reliablility you actually need, which is why it's essential that you understand your requirements. You also don't have to do all the work yourself. In fact, you probably shouldn't. You should get someone else to work on the test side of things.

    By the way, one essential subsystem to modularaize is the allocation of resources. You will find a lot of defects just by inserting a memory allocator that occassionally simulates out-of-resource conditions. There are tools to do this, but they don't seem to be portable to different operating systems.

  33. good coding techniques by Pr0xY · · Score: 4, Informative

    first and foremost, use good coding techniques. This means use exception handling where appropriate, use standard containers over hand rolled data structures (prefer std::string over char arrays, this will help prevent almost all common string based buffer overflows alone), and follow good style guidelines.

    As for a GUI programming, if you are strictly tied to c++, i would recommend QT (www.trolltech.com) they have a fabulous API (takes getting used to, but it makes sense once you do). Nice part about QT is that it is source portable to just about every major platform (X11, Win32, Mac).

    It is possible to write reliable, fault tolerate code in c++ (realize please that perfect code is impossible in any language), it just has to be well thought out and done right.

    proxy

  34. "3. NEVER ASSERT!!!!" ...? by neutralstone · · Score: 2, Insightful

    I really think you had better qualify this.  IMO, assertion failures do not *cause* problems; they are messengers, and the message is always this:  "Your program is broken."

    I don't think you want to *recover* from a broken state.  I think you want to debug it -- find out what went wrong, fix the code, recompile, test, and re-deploy.

    Because, if you get to the point where an assertion fails, it means the state of the program is corrupted, and therefore you can't trust any part of it; e.g., you can't trust error-recovery code to be well-behaved.  The best you can do is bring everything to a halt and fix the bug.

    There are rare exceptions (no pun intended) to this rule, but for the most part, if you write out a condition and say, "if this is false, then the program has a bug", then you have some explaining to do if you *don't* want to use an assertion.

  35. A few guidelines by pornking · · Score: 4, Insightful

    The suggestions I have seen here so far seem to boil down to "Don't do it that way". Sometimes that's not possible. If it truly has to be C++, and it truly has to be as fast as possible and as bug free as possible, there are a few guidelines that can help:

    1. Unless the GUI will be I/O bound, and that's unlikely, try to write it in a safer language that has better GUI support.

    2. Make all your classes small and simple, and create test harnesses that are as complete as possible. Try to make the classes simple enough that they can be individually tested in such a way that all code paths are exercised.

    3. Check your arguments. This includes checking for invalid combinations, and arguments that are invalid given the state of the object.

    4. Don't use new or pointers directly. If there may be multiple references to an object, then reference count it and create handle classes that hold the references so all instantiation is controlled, and all destruction is implicit. Make these handles STL compatible, and never pass around pointers to them.

    5. Try to design the application to fail fast and recover from failure. For example, maintain the state of work being done in discrete transactions that can be aborted if a failure is detected. This can be on disk or in memory depending on your performance needs. This could be combined with the ability to restart the app in a new process and have it pick up where the last one left off.

    6. Have the app keep track of its memory usage, and be prepared to recover from memory leaks, possibly by restarting as in item 5.

    7. If the compiler you're using supports structured exceptions, then use them. They can degrade performance a bit, but they can also enable you to recover from NULL pointer exceptions.

    8. If you have multiple threads, then to avoid both the performance hit from context switches and the chance of deadlocks, don't let them access the same data directly. Instead, have them communicate through lock free queue structures. That way, all your main threads can pretty much spin freely. Spawn worker threads for any I/O or other operations that can block. A context switch can take as much time as thousands of instructions. You want to use as much of every time slice as possible.

    9. Keep the number of main threads down to the number of CPU's or less. That way, except for the times when the CPU is being used by the OS or other processes, (should be relatively rare) each non blocked thread gets its own CPU.

    10. Have an experienced QA team, that understands their job goes beyond unit testing.

    Now here's a few that are always important, but for what you want to do, they become critical.

    11. Have the design laid out at least roughly before you start.

    12. If at all possible, don't let requirements change in midstream.

    13. Overestimate the time it will take very generously. You will probably still be crunched.

    --
    pornking
  36. Recoverability by marko123 · · Score: 3, Insightful

    Rather than investing too much time and effort in creating a complicated crash-free program, just make sure your application can recover from a crash, and then use a process management application that restarts the program on it's node when it is detected to not be running properly.

    It's simple to write a 100% correct program that checks the health of your main application, and restart it when it isn't responding.

    --
    http://pcblues.com - Digits and Wood
  37. On Garbage Collection and Stability by CharonX · · Score: 3, Insightful

    I partially agree.
    If your code is unstable in a way that memory leaks and segmentation faults are not only a "remote possibility" but a - even if only rarely - reoccuring event, then any safeguards you implement won't be overly sucessfull, unless you fix the code that causes the errors first. (Disclaimer: There is no perfect code. Even if there were no bugs in the code, the program has still the "remote possibility" to crash due to errors in the hardware / OS)

    That said, garbage collection or not is a different discussion. Some say it is bad and breed lazy programmers, while others argue (I amongst them) that it is a terrific tool for designers, since it almost eliminates the occurance of memory leaks (unless you do some really bad programming) and it might even speed up your program

    --
    +++ MELON MELON MELON +++ Out of Cheese Error +++ redo from start +++
  38. Consider Ada by kafka.fr · · Score: 2, Informative

    Using the good-old Ada programming language (for which a new standard should be issued this year), you can go closer to what you're looking for (though I'm not sure your goal is realistic).

    Here's a pointer to the new standard : http://adaic.org/standards/05rm/html/RM-TTL.html

    With Ada:

    • you can call externally-defined C/C++ functions (see annexe B);
    • you get quite advanced runtime-checks;
    • you have plenty of exceptions to use to catch errors (see sections 11 and section Q.4);
    • you have tasking (more or less threading) implemented inside the language (see section 9);
    • a specialized annexe is targetted toward distributed system (annexe E);
    • specialized annexes for high-integrity systems (annexe H) and real-time systems (annexe D);
    • ...and more, much more.

    It probably won't solve every of your problems, but it might help.

    For a free, quite strong Ada compiler, have a look at https://libre2.adacore.com/ (it's based on GCC).

    Oh yes, Ada is a statically, strongly, strictly typed language (e.g. the compiler won't let you assign an integer to a float variable). My opinion is that it's a Good Thing for critical programs. Useless to restart a "type war" on this subject ;-)

    Good luck.

    1. Re: Consider Ada by Black+Parrot · · Score: 2, Informative

      > Oh yes, Ada is a statically, strongly, strictly typed language (e.g. the compiler won't let you assign an integer to a float variable). My opinion is that it's a Good Thing for critical programs.

      Yes, it's very frustrating when you first start using it because it makes you say what you mean and mean what you say, but once you get over that you find yourself writing programs where certain kinds of bugs simply never happen.

      The strong typing (no, strong, not the fluff some other languages call "strong") and run-time checks work to move error-discovery forward. That is, you find stuff a compile time that most languages leave you to find at run time, and you find stuff at run time that most languages leave you to (hopefully) notice that you're getting incorrect outputs.

      --
      Sheesh, evil *and* a jerk. -- Jade
  39. Nooo!!! No separately restartable modules!! by Handyman · · Score: 2, Interesting

    In my experience decoupling and automatic restarting is a recipe for failure. You set yourself up for all sorts of race conditions. For instance, if a module is unresponsive for a while but not crashing, do you restart it? And if you do, what if the original module finishes its grand execution plan and comes back up after a minute?

    No, I'd go for:
    * A "monolithic" application with module separation provided by OO design. At least you know that either your whole application is there, or it isn't. No inconsistencies between modules because of individual module re-starts, and if the app breaks, restart the whole thing. Starting the app is the code path you've tested, restarting separate modules usually isn't (and even if it were, there's usually 2^27324 different situations to test, i.e., all possible combinations of modules failing in any sort of way).
    * Use smart pointers exclusively, preferably Boost's shared_ptr. Use weak pointers (Boost provides an implementation for that as well) to automatically break reference cycles.
    * For error handling, use exception handling exclusively. Incredibly many bugs are caused by ignored return codes.
    * Use "auto" objects for all resources that you acquire and that need to be released at the end of a code section. Cleanup that doesn't happen when a code path encounters an exception can cause resource leakage, instability and hangups (locks, anyone?). In my programming practice, when I allocate a resource (memory, refcount, open/close a recordset, etc.), I always wrap it in an auto object immediately, so that I can forget about managing it through all the code paths that follow.
    * Use the correctness features that the language provides: write const-correct code from the start.
    * Use automated testing right from the start, both unit testing and integration testing. If you don't do this, you will be forever tied to whatever bad design decisions you make in the first months of the project. Automated testing allows you to always make large implementation changes, giving you confidence that it will not break existing behaviour.

  40. Obvious ! by Thomas+Miconi · · Score: 5, Funny

    What this guy really needs is the time-tested, tried-and-true Waterfall development process !

    Thomas-

    1. Re:Obvious ! by GroovBird · · Score: 2, Insightful

      Well since the parent is posting a link to a satirical website I wouldn't moderate his post as "informative" but oh well.

  41. I don't know why this dominates the first page... by hummassa · · Score: 4, Insightful
    of the thread... I'm appalled. I'll answer to this, when I would really like to answer to the main post, to maximize chances of you reading me.

    Question 1: what strategies should a developer take to insure that the resulting program is as crash-free as possible?

    Answer:

    a. Use OO techniques and maintain all objects in your system extremely simple; furthermore, maintain all methods in your system extremely short, well-contained, well-defined.

    b. Don't use C++ arrays, ever. Especially not for strings. Use and abuse the STL.
    copy( istream_iterator<int>( cin ), istream_iterator<int>(), back_inserter( v ) );
    is just plain beautiful IMH?O.

    c. Check extensively the behaviour of your constructors and destructors.

    d. Make a object-lifecycle diagram of each class you program. In the diagram, relate it to the neighboring classes (parents, children, siblings, classes involved in design patterns with, classes aggregated, classes value-aggregated, classes where this is aggregated or value-aggregated)

    e. Use, carefully, and always when possible, smart pointers. Remember std::auto_ptr is your best friend -- its limitations are a defining part of its strength. Remember boost::shared_ptr is also a good friend, but its cousin boost::intrusive_ptr is even more friendly -- but use one of those (and their other cousins scoped_{ptr,array}, shared_array, weak_ptr) only in the (rare) cases where auto_ptr does not apply.

    f. As a corollary to (e) above, use boost. This is really an extension of (b), too.

    Question 2: How can I actually implement such a decoupling?

    Answer:

    I would use a simple, socket-base, take-my-data, gimme-my-results scheme. It would be network-distributable, easy to detect if some service is or isn't alive via timeouts... If you want something more sofisticated/RMI-like, SOAP (with binary XML or compressed) may be an option. The simpler the better IMHO.

    Question 3: are there any software _design patterns_ that specifically tackle the stability issue?

    Answer:

    All of them? IMHO, DPs can represent huge tool to increase the stability of a system. Take a look athere [WARNING: PDF] (and in the bibliography) for some ideas.

    I know many of my posts were self-marketing lately, but if you need someone to work with you, I'll be happy to send you my resume... write me at hmassa (at) gmail.
    --
    It's better to be the foot on the boot than the face on the pavement. ~~ tkx Kadin2048
  42. Re:Question from a Newbie by inter+alias · · Score: 2, Informative

    Funny you should that, I read this: D just 5 minutes ago.

  43. Coincidence from the toilet by defile · · Score: 3, Insightful

    In a desperate rush for some reading material for the toilet, I grabbed what must be a 5 year old C/C++ User's Journal from a storage room. The theme of that month's issue was MULTITHREADING.

    I thumbed through it and came across an interesting article ``ALWAYS HANDLED ERROR CODES''. The idea being that a lot of errors can go undetected because programmers are lazy about checking return values. And why not, who bothers checking printf()'s return value, for instance?

    Simple enough design. The object constructor sets the result, the destructor will abort() the application if the Checked variable is false. The overridden == and != operators evaluate the result, and also set the Checked variable.

    In your functions, instead of return SUCCESS; you write return ErrorCode(SUCCESS);

    Wondering if anybody does this. If I needed something ULTRA STABLE I guess I might...

  44. Use STL; avoid pointers; avoid fixed buffers. by Theovon · · Score: 3, Informative

    You can avoid some of the pitfalls of C++'s need for manual memory management and other problems by simply avoiding them. For instance, never do memory management yourself. How? By using STL containers to do it all for you. Next, avoid fixed arrays. Again, let STL do it for you. And, above all else, never do anything where you don't restrict the length. Since you're using STL for arrays, you're good to go there, and you won't end up running off the end of a character array (because you don't use them!). So what you're left with is doing I/O properly. Always limit the the amount of data you read to the buffer size you have allocated.

    I'm sure that there's tons I've left out, but this has worked reasonably well for me. The only problem is that STL can be slow. Sure, map may be O(log(n)), but the constants are huge. Unfortulately, for practical reasons, performance and security are often inversely proportional.

  45. misc. advice and a small rant? by EsbenMoseHansen · · Score: 2, Interesting
    C++ takes a lot of platform-specific work to become portable,

    Where do people get this idea? I have ported quite a few applications, and usually the porting done by locating the libraries you need on the new platform, and fix a few oddities in the current platform (like closing sockets in z/OS or switching to unsafe multitasking (p-threads) on windows. Porting to linux is so trivial that I often do it just to get access to the superior tools available there, especially valgrind. GUI is the exception, of course, unless you use a x-platform kit from the beginning.

    Which leads me to my recommendations, in no particular order

    • Use Valgrind. A lot
    • Use a good toolkit. If GPL is acceptable, consider QT.
    • Consider a "packet" or a "transaction" based approach, that is design every application to take in a package, process it and return/store the result. This sort of applications are easier to automatical test
    • Avoid huge application anywhere... no more than 100 classes per application, no more than 1000 lines per class
    • Use automatic, integrated unittest
    • Use automatic, daily run integration/function tests
    • Do not accept complication designs
    • Avoid close sourced libraries. Be aware and fix library issues.
    • Avoid incompetent developers on critical components. Let them make the GUI/statistic/other fringe components.

    The above approach works for me. You mileage may vary.

    --
    Religion is regarded by the common people as true, by the wise as false, and by rulers as useful.
  46. Build it around a stable database by Mutatis+Mutandis · · Score: 2, Informative

    I am by no means a specialist in this field and of course I do not know whether your project actually allows this approach.

    But if I were asked to do this, I would take a database (a stable release of MySql is what I would choose) and use it both as the persistent object storage and communication module. The GUI and the number-crunching module(s) would be set up to primarily communicate through the database, rather than directly with each other. A task state/queue table in the database would inform the modules what tasks have not been assigned yet, are running, are complete, have not returned in the expected time, or have failed. This would make it asynchronous and highly traceable; databases are (supposed to be) good at managing the interactions between multiple user processes and still maintaining data integrity. Admittedly this is not the best approach if you want your results real-time.

    The central managament of the processes could be kept minimalistic and simple, and "therefore" robust: Some very simple communication with number-crushing processes to test whether they are alive (a TCP/IP socket read-write might do), re-opening a task that has not returned in an expected time period (if its process still returns later, the newly started process will have to detect that its work was already done, and discard its results instead of writing them back), and perhaps signalling critical task completion to users (by GUI message, e-mail, text message, ...). The central management would not have the startup responsibility for distributed number-crunching modules, that would remain with the local servers they are running on. Such a process can then "knock on the door" of the database, register its presence, and take the next available task, or wait until one is available.

    The persistent form of the core data structures would be in database tables, but the modules would of course have their share of the data in memory as class representations of the data structures, defined to be initialized from the database tables and written back to them. These class representations of the data structures then could be in a common library shared by the different modules, but alternatively you might opt for different class representations for e.g. the GUI and the number-crunching modules if that is more efficient (it often is) and even write them in different languages if that is more convenient. I admit that that adds to the amount of code and therefore to the amount of bugs. On the other hand, you could write two "completely" independent implementations of the same task.

    If your number-crunching is complex and long, then evaluate whether you can write back intermediate states to the database as a recovery point, or even split the calculations in completely independent modules, each one starting and ending with a given database state. The desirability of this depends, of course, on the balance between I/O and processing costs. If you have modules that are relatively simple and safe but need to work quickly through a large amount of data, you could consider database stored methods for these; not very distributed but it reduces the amount of I/O and they can easily be called by client processes.

    The database does not care in what language the different modules are written, so you can then write every one in the language that is most appropriate. For example, there may be no reason at all to write (parts of) the GUI in C++ -- and that is something I would try to avoid. If performance allows it, I would use Java for the GUI, both for portability and simply to avoid the mess of writing user interfaces in C++; in my experience that does not tend to be the most stable solution.

    For the C++ part I would start by structuring pretty strongly; write a large number of simple classes instead of a smaller number of complex ones, and test every class before you move on to the next level. The "salami approach" works well if you plan it well. It is perfectly possible to write very ro

  47. Re:I am invoking Greenspun's 10th law by mkcmkc · · Score: 2, Insightful
    [Y]ou are assuming that a language like Python translates line-by-line into C++. Does it?

    I've been following this methodology (Python first, then C++ as/where needed) for a number of years. In all of that time, I've only had one application where I ended up needing to drop into C++ at all. In that case, a couple of pages of Python did translate into a couple of pages of C++, virtually line for line. Heavy use of STL allowed this, as there are a lot of data structures and algorithms there that map more-or-less directly to Python. The main problem was that the STL tended to be either buggy or to have razor-sharp edges upon which to cut oneself.

    Python is generally a win because (1) you can write the same working functionality much faster than in C++, and (2) the specifications for apps tend to vary wildly over time, so a high-level language lets you go with the flow.

    --
    "Not an actor, but he plays one on TV."
  48. Have you have flown on a commercial airline? by EMB+Numbers · · Score: 4, Informative

    Have you have flown on a commercial airline in thelast 30 years? If so, you trusted your life to software.
    Thare is a standard called DO-178B Level A that applies to aircraft software upon which lives depend. There is a saying in the commercial avionics business: "Nobody has ever died from software failure on an airplane, yet." There have been some accidents where software played a role, but I won't quibble with that now.

    The point is that safety critical software is developed routinely. It has been developed in asembly language. It has certainly been developed in Ada, C, and sub-sets of C++. It is expensive. Validation of avionics software and certification in an aircraft can easilly cost an order of magnitude more that just writing the software, and writing the software using required processes and producing required artifacts is not cheap either.

  49. Re:Bullshit by TheNetAvenger · · Score: 2, Interesting

    Bullshit. C++ written well is portable by default (between windows and linux). There are a few minor issues between linux and sgi.

    I agree.

    This is by nature one of the biggest strengths of C and C++, how someone could conclude that by using C++ adds some sort of complexity in cross platform development actually amazes me.

    If it adds complexity, in comparison to what? I would like to see the poster above you explain what is actually easier to use for diverse application development that is actually better at cross platform.

    And if they start with Java, la la, then they need to get a life and see what JAVA is built upon itself.

    C and C++ is a great solution for cross plaform development, look at the nature of Linux, BSD, and even NT and then ask why they are as portable as they are. Do people think these OSes would be more portable in another language?

    Take Care.

  50. TSP is the answer. All Hail TSP. by diverscuba023 · · Score: 2, Funny

    Ah, but test driven development flies in the face of the new government backed, SEI approved software development silver bullet called TSP (Team Software Process). And by following TSP you too can consider just how much better it is than test driven development while waiting for your co-workers to inspect your code for a few months.

  51. Learn to use STL and don't *ever* type "new[]" by Joce640k · · Score: 3, Informative

    1) Learn to use STL.

    Do *all* memory management via STL vector/string.

    2) Don't ever type "new[]/delete[]".

    Just don't do it. Not. Ever. Use std::vector instead.

    "Arrays are evil" - the C++ FAQ.

    PS: You can still use malloc()/free() but only as a last resort in low-level classes which are designed for data storage.

    3) Get a reference-counted pointer and use it.

    Automatic memory management...'nuff said.

    4) Attach an alarm bell to your "~" key.

    If you're writing destructors for classes which don't control system resources (eg. files) then you're probably doing something wrong - see notes 1, 2 and 3.

    --
    No sig today...
    1. Re:Learn to use STL and don't *ever* type "new[]" by Retric · · Score: 2

      IMO: Use Java(ick)/Pascal/(stable high level lanuage) and link to your own custom C++/ASM lib for the stuff that needs to be *fast*.

      By spliting the app into more than one language you end up with a clean code seperation between the *fast* code and the *stable* core. Chances are you don't realy need to work out complex thread communication all that often, but if people think the App needs to be fast then they will start optimising stuff that they have no reason to thouch. If this is not fast enough profile the code and see what you need to work on. Chances are your app is going to spend over 90% of it's time on less than 10% of it's code so you are free to keep everything else clean and only make a mess of that fraction.

  52. Re:I don't know why this dominates the first page. by Chemisor · · Score: 3, Insightful
    > copy (istream_iterator(cin), istream_iterator(), back_inserter(v));
    >
    > is just plain beautiful IMH?O.

    I'm sorry, but I just can't agree. It might appeal to a mathematician who wants to see everything use functional notation and hates every language except lisp, but to a non-abstract-elite-ivory-tower-mathematician this is absurd. cin is not an array of integers and the use of the adapter obfuscates the fact that you are using a conversion from a char array to an int. The back_inserter also makes it harder to see where the data is going by losing "v" in it. Many would also frown at it for taking a non-const reference, although since it is a standard adaptor it is probably ok.

    C++ programmers are often unnaturally attached to efficiency and have to be watchful for template bloat. Your copy generates 88 instructions, whereas an equivalent iterative solution is only 33 instructions long, most of them belonging to the inlined push_back. Not only is the generated machine code smaller, but the source code is smaller as well, and is far more readable, making the algorithm obvious at a glance to any procedural programmer, who make up the majority outside the hopelessly out-of-touch with reality academia.

    int n;
    while (cin >> n)
    v.push_back (n);

    Academics love integer and float arrays because that's what they usually work with. Scientific simulations produce data in that form and require processing programs that take something from a data file, crunch some numbers, and output something to cout. In the real world people work on user interfaces, databases, and other complicated things, where one normally works with arrays of objects rather than numbers. If you ever tried to apply a functional algorithm to a vector of objects, trying to manipulate some member variables or call a member function, you would know that the result is so hideous that it isn't even worth considering. There is a reason people prefer iterative solutions; they are how the real world works. Reality is algorithmic, not functional, and so are user specifications for the things they want done. Trying to cram them into an abstract mathematical functional model is insanity.

    > Use, carefully, and always when possible, smart pointers.
    > Remember std::auto_ptr is your best friend

    Most of the time, no. While I would not deny the utility of auto_ptr in localized situations manipulating the object state during reallocation, its constant use indicates lack of understanding of object lifecycle in the program. It is fashionable in Java to create objects left and right, without consideration of who is supposed to own them. Hey, just let the garbage collector take care of it! Who cares how long the object lives? Obviously, such immature mentality produces plenty of memory leaks for which Java is so infamous. In a good design object ownership is strictly defined. Objects belong to collections that manage their lifecycle. There ought to be no "dangling" objects that just "hang there". If you don't know to which collection the object belongs, you have no business creating it. If you think your objects are "special", you haven't thought beyond their internal functionality or considered where it fits in your overall design.

    > Question 2: How can I actually implement such a decoupling?
    > I would use a simple, socket-base, take-my-data, gimme-my-results scheme

    And thereby slowing your program to a crawl? There is a reason people use CORBA and the like: those frameworks optimize distributed object calls to avoid network hits, often being able to reduce the overhead to be equivalent to a virtual function call. Furthermore, networked applications have their own set of complexities and security considerations. You get to keep an open port somewhere, handle authentication (becase wherever there's an open port, there will be malicious connections), and extensive data validation (for the same reason). While these problems are applicable to dis

  53. QuickCheck by Paul+Johnson · · Score: 2, Informative
    Try using something like QuickCheck . The original version was for Haskell, but you could easily adapt it to work with C++.

    The idea is simply to define the "space" of legal inputs for each module and the correctness criterion for each input, and then generate random inputs based on the spec. This is far more effective than traditional hand-coded test data at both unit and system test levels, and as an added bonus the test spec doubles as a formal specification of the correct behavour that coders can actually work from. This is similar to the XP practice of "test-driven development".

    Paul.

    --
    You are lost in a twisty maze of little standards, all different.
  54. Re:I don't know why this dominates the first page. by Wolfier · · Score: 2, Interesting

    I'm sorry, but I just can't agree. It might appeal to a mathematician who wants to see everything use functional notation and hates every language except lisp, but to a non-abstract-elite-ivory-tower-mathematician this is absurd. cin is not an array of integers and the use of the adapter obfuscates the fact that you are using a conversion from a char array to an int. The back_inserter also makes it harder to see where the data is going by losing "v" in it. Many would also frown at it for taking a non-const reference, although since it is a standard adaptor it is probably ok


    Not understanding something is one thing, but not understanding something so let's reject it as being "elite-ivory-tower" and "academic" is another. I've seen a lot of buggy C++ code being rewritten employing this style in obvious places - many defects were automatically addressed.


    Reality is algorithmic, not functional, and so are user specifications for the things they want done. Trying to cram them into an abstract mathematical functional model is insanity.


    I disagree. Reality is reality. Algorithmic or Functional are just ways people look at it. Aren't "algorithmic" also abstract? Isn't "object-oriented" abstract as well?

    By the way, using your vocabulary, I view the world as a mixture of "algorithmic" and "functional". No pure anything can describe the world, in my opinion.


    Reality is algorithmic, not functional, and so are user specifications for the things they want done. Trying to cram them into an abstract mathematical functional model is insanity.


    Being functional or algorithmic has *NOTHING* to do with one being "more mathematical" and the other "less mathematical". I advise you, that your use of the common peoples' fear for mathematics in your arguments is not going to help.


    C++ programmers are often unnaturally attached to efficiency and have to be watchful for template bloat. Your copy generates 88 instructions, whereas an equivalent iterative solution is only 33 instructions long.


    Templates, being code generators, differ by nature to hand-tuned codes. So your code generates only 33 instructions vs the template's 88. Great - now tell me - which architecture? What compiler? What version of that compiler, and whose STL are you using, and which version of THAT?
    And before you count the instructions, did you realize that this code is waiting for keyboard inputs, therefore what you're doing is unnecessary (and obviously premature) optimization?


    While I would not deny the utility of auto_ptr in localized situations manipulating the object state during reallocation, its constant use indicates lack of understanding of object lifecycle in the program.


    How does the constant use of auto_ptr relates to the understanding (or the lack thereof) of object lifecycle? Sorry, but understanding object lifecycle the liberal use smart pointers are not mutually exclusive.


    It is fashionable in Java to create objects left and right, without consideration of who is supposed to own them. Hey, just let the garbage collector take care of it! Who cares how long the object lives? Obviously, such immature mentality produces plenty of memory leaks for which Java is so infamous.


    It is fashionable *among incompetent* Java developers to create objects left and right which make their programs memory hogs. It is also fastionable for *incompetent* C++ programs to forget deallocations leaking memories. What's your point? This mentality, immature or not, is not unique to managed languages.

  55. My Top Ten by dubl-u · · Score: 2, Informative
    THere is no silver bullet for what you describe other than sound development practices. The best results in this area are acheived by teams who are constantly refining their processes based on lessons learned in previous software iterations.

    Bulletproof code isn't cheap, but it can be done.


    This is the most insightful comment I've seen so far. Particular tools can fix particular problems, but that's the easy part. The hard part is finding and noticing the problems, so that you know to look for (or make) the tools.

    My teams have in-production bug rates well below one per developer-month. Here are the ten things I think are most important:
     
    • test, test, test (manually) - The only way to be sure that something works in production is to repeatedly and frequently prove that it works in conditions as bad or worse than production.
    • test, test, test (automatically) - Write unit tests, integration tests, end-to-end tests, and load tests. Write as many as you can in test-driven development style, where the test comes before the code that makes the test pass. My code bases are circa 50% test code, with 95% code coverage.
    • make testing the default - Make your tests run with one simple command. Then automate it so that all tests run on every checkin.
    • integrate frequently - Keep your colleagues up to date: check in every few hours. Breaking your work down into discrete bites takes practice, but it's worth it.
    • work as a team - Get everybody in one war room. Make it a great environment to code in. A big source of bugs is misunderstanding, and having everybody together eases intentional communication and creates a lot of tacit communication.
    • code in pairs - Yes, this takes some getting used to. But pair programming is just cranking up code review. Now that I've gotten used to it, I think solo coding is no fun: I know I'm making stuff that's less good than it could be, but until I come back to it a week or a month later, I can't tell what the problem is.
    • work sane hours - Once you start doing these other practices, you will discover that there is no point in staying late: as productivity drops and your error rate increases, marathon sessions just dig the hole deeper. Drunk drivers think they are just fine; so do tired coders. Both are wrong.
    • regularly look back and ahead - Every Friday at 4, bring in cold beers and talk about what went well and what didn't. No need for action plans or GANTT charts; just talk.
    • aim for perfection - A lot of shops consider it perfectly normal to have hundreds and hundreds of bugs in the database. This may be normal, but it's not necessary. There are a number of teams working at or below the level of one bug per developer-month, and yours could be one of them.
    • remember: quality can pay for itself - I have seen shops where developers average 50% of time in the debugger, and then complain that they have no time for code reviews and unit testing. Even if they can't connect the dots, you can. I spend perhaps five minutes a day in the debugger, and every time I do it, it's a sign that I'm missing a test and that the design could be clearer.
  56. Re:I don't know why this dominates the first page. by Chemisor · · Score: 3, Insightful

    > Not understanding something is one thing, but not understanding something
    > so let's reject it as being "elite-ivory-tower"

    I did not say I did not understand it. I said I did not like it. I do not like it because it does not fit with the reality of computer operation, as discussed below.

    > Reality is reality. Algorithmic or Functional are just ways people look at it.

    On the contrary, you can see reality being algorithmic. Things happen one after another. To type "algorithmic", you depress a, l, g, etc. in order; you don't declare a set of letters, fill it with appropriate values and throw it at the computer. When you receive a specification for your program, it will say something like "get this from the user, then do this, then do that, then print out the result". No specification is ever written in functional notation outside the academic world.

    More importantly, the computer itself works algorithmically. It does one thing, then another. No computer has ever worked functionally, and no computer ever will. All of them will decode and execute a sequence of instructions, and if you refuse to write your code likewise, you're only adding translation overhead.

    Even in the hallowed halls of science overuse of the functional notation creates serious problems. The entire hodge-podge nonsense we call quantum mechanics stems from the attempt to describe a complicated system as a function. Instead of trying to get a set of time-value maps for the whole system, it would be more appropriate to look at the system's constituent parts and algorithmically simulate them through time. That way you wouldn't get any "spooky action at a distance", stuff being there and not there at the same time, and all other equally ridiculous denials of reality.

    > I advise you, that your use of the common peoples' fear for mathematics
    > in your arguments is not going to help.

    I wasn't using that argument, but, now that you mention it, it is a reasonable one. Most programmers couldn't care less about higher mathematics, and, even if they were forced to study it in college, they likely have forgotten it all by now. Computer algorithms require minimal mathematical background. The most I ever used was a bit of calculus to write scan-conversion routines. So, whether from lack of practice, or from lack of interest, most programmers will prefer you didn't drag them into the world of useless mathematics. (and I use the word literally here)

    > Templates, being code generators, differ by nature to hand-tuned codes.
    > So your code generates only 33 instructions vs the template's 88. Great
    > - now tell me - which architecture? What compiler?

    That is quite irrelevant in this case. istream_iterator notation generates extra code for reasons that will not go away no matter how hard you try to optimize it. Yes, I might be able to write an istream_iterator that would have no overhead over my iterative version, but it will not be standard compliant. The istream iterator has to read on construction; it has to store the read value; it has to be constructed, since it must keep a reference to the source stream; it has to handle special cases, like the end-of-file, and the subsequent conversion to the end iterator value. However good you might be at optimization, you will not be able to discard these and still be compliant with the specification.

    Also, which compiler or architecture you use will not make all that much difference in the size of the compiled code. I guarantee you that your functional copy will never generate smaller code than my iterative loop, no matter what compiler you use or what architecture you compiler for. There is a certain amount of work to be done, and my version does less work. It is as simple as that.

    > And before you count the instructions, did you realize that this code is waiting
    > for keyboard inputs, therefore what you're doing is unnecessary (and obviously
    > premature) optimization?

    First, you should note that I

  57. Sorry, *not* in C++ by HermanAB · · Score: 3, Interesting

    You cannot write highly stable code in C++, due to design flaws in the language. For this reason, the FAA doesn't allow C++ for use in aircraft systems. You can improve the situation with the use of a garbage collector though, but if stability and safety is critical, then you should use ANSI C. See this: http://www.hpl.hp.com/personal/Hans_Boehm/gc/issue s.html

    --
    Oh well, what the hell...
    1. Re:Sorry, *not* in C++ by landtuna · · Score: 2, Informative
      For this reason, the FAA doesn't allow C++ for use in aircraft systems.

      You might want to let them know about that.

      Aeralib

  58. Re:While I can certainly respect your opinions, by Chemisor · · Score: 3, Interesting
    > I guarantee you that I rather encounter a
    > for_each(components.begin(), components.end(), _1.disable())

    It is never that simple. The fact that you can't do what you've typed is one of the reasons I dislike it so much. What you really need is:

    for_each (components.begin(), components.end(), mem_fun_ref (&CComponent::disable));

    Things suddenly got uglier, didn't they? But wait, what if you need to call a function with an argument? Gotta use a bind2nd adaptor to wrap it, and then it becomes:

    for_each (components.begin(), components.end(), bind2nd (mem_fun_ref (&CComponent::SetParameter), value));

    Wait 'till you try to explain to some maintaining programmer how to untangle that! Oh, and just for laughs, try to debug this thing. Put an assert in SetParameter, and you get a lovely callstack from gdb:

    (gdb) run
    Starting program: /home/user/tmp/tes
    tes: tes.cc:18: void CComponent::SetParameter(int): Assertion `!"Check out the callstack!"' failed.

    Program received signal SIGABRT, Aborted.
    0xffffe410 in __kernel_vsyscall ()
    Current language: auto; currently c
    (gdb) where
    #0 0xffffe410 in __kernel_vsyscall ()
    #1 0xb7d36126 in *__GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:67
    #2 0xb7d37b40 in *__GI_abort () at ../sysdeps/generic/abort.c:88
    #3 0xb7d2f610 in *__GI___assert_fail (assertion=0x6 <Address 0x6 out of bounds>, file=0x6 <Address 0x6 out of bounds>,
    line=6, function=0x80495a0 "void CComponent::SetParameter(int)") at assert.c:83
    #4 0x080485f6 in CComponent::SetParameter (this=0x804b008, arg=42) at tes.cc:18
    #5 0x08048ac3 in std::mem_fun1_ref_t<void, CComponent, int>::operator() (this=0xbfe2cacc, __r=@0x804b008, __x=42)
    at stl_function.h:826
    #6 0x08048ae8 in std::binder2nd<std::mem_fun1_ref_t<void, CComponent, int> >::operator() (this=0xbfe2cacc, __x=@0x804b008)
    at stl_function.h:446
    #7 0x08048b0c in std::for_each<__gnu_cxx::__normal_iterator<CCompon ent*, std::vector<CComponent, std::allocator<CComponent> > >, std::binder2nd<std::mem_fun1_ref_t<void, CComponent, int> > > (__first={_M_current = 0x804b008}, __last=
    {_M_current = 0x804b00c}, __f=
    {<> = {<No data fields>}, op = {<> = {<No data fields>}, _M_f = {__pfn = 0x80485c4 <CComponent::SetParameter(int)>, __delta = 0}}, value = 42}) at stl_algo.h:158
    #8 0x08048740 in main () at tes.cc:26
    (gdb)

    Now that's something to scare newbie programmers with! Oh, and forget about putting a breakpoint inside the loop; templated functions aren't targetable until executed.

    > in some code I need to maintain then to encounter
    > for(i = 0; i < components.count(); ++i) components[i].disable()

    So why not just use an iterator loop? for_each does not have a monopoly on it:

    foreach (compvec_t::iterator, i, components)
    i->disable();

    (foreach is a macro I wrote because I use this construct so often)

    > first form permits, for instance, components to be a linked list or even a hash.
    > The second is implementation-dependent and if you change the underlying data
    > structure, you'll have extra work to refactor.

    If you use iterator loops, this wouldn't happen to you.

    > I once worked, changing all instances of SomeObject* to auto_ptr
    > eliminated altogether 35 bugs we had lurking in the BTS for a long, long time,
    > with less than one day of work (strange, delayed, errors were suddently
    > transformed in EARLY null-pointer dereferences

    Why were you using SomeObject* in the first place? When I was advocating moderation in the use of auto_ptr, I wa