This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Error Stack Submitted by

I have tried, and decided I didn't like, exception handling. I find it cumbersome and awkward. I have also found, trying to reuse code, and linking with libraries, and all the rest, that they never quite give me errors back the way I would like. It depends, in the end, on the application - not the library - on how it should be reported - logged/printed/message box/html/etc... Imparticularly, if I use one library, and that uses another, and that reports an error, I usually find myself writing error handling routines three times the length of the rest of my code, trying to figure out which library has stored the error that actually happened so that I can do something with it. As an experiment, I tried putting a string in each class I wrote, called ErrorStr, and a function which returned it. Everything returned success/failure and you could query the error string for the reason. If a function in a library was returned an error by another function, it would concatenate its version of the problem with the one returned, so in the end I see everything, ie: Error loading data : Error loading models : Failed to initialise textures : file [skin.jpg] not found That is nice, and more importantly I get it very easily, it is up to me what I do with it, and it effectively shows me the calling sequence that leads up to it. However, for a class such as Vec3f, which holds exactly 3 floats, no more and no less, it doesn't work, and for a class which I have, say, 300 instances of, its slightly less than efficient. The next phase, then, was something globally available to everything in the project - an error class which stores the error, can be added to, and can be queried for a formatted string. So that libraries can work independantly, but not tread on each others toes when used together, this is a singleton, not a global variable. To make it easier to push errors onto the stack without running off the right hand edge of your screen, there are also a couple of global helper functions. ErrorStack.h and ErrorStack.cpp are all you need. The other stuff is purely an example/test of the class.
I hope someone finds that usefull. It isn't actually intended to be copied as is - it is a fairly simple design pattern for handling errors and should be changed to work the way you do (error numbers / string tables / templated singletons / whatever)

Download Associated File: errorstack.txt (5,445 bytes)

    //COTD (09/27/2001) submitted by Squint [flipcode@squint.clara.co.uk]

    // ErrorStack.h

    #ifndef ErrorStack_H
    #define ErrorStack_H

    ////////////////////////////////////////////////////////////////
    // Class header for ErrorStack
    ////////////////////////////////////////////////////////////////

    #include <vector>
    #include <string>
    #include <stdarg.h>

    class ErrorStack
    {
        private:
            ErrorStack() {}                                 // Constructor
            virtual ~ErrorStack() {}                        // Destructor
            ErrorStack (const ErrorStack &);                // Singleton so no copy constructor
            ErrorStack & operator= (const ErrorStack &);    // Singleton so no assignment operator
        protected:
            std::vector <std::string> Stack;                // This stores the error messages
        public:
            static class ErrorStack * Instance () { static ErrorStack * TheInstance = 0; if(TheInstance) return TheInstance; else return (TheInstance = new class ErrorStack); }
            void Clear();                                   // This empties the stack
            void Push(const char * fmt, ...);               // This adds an error message to the stack
            void Push(const std::string & str);             // This adds an error message to the stack
            std::string Report();                           // This returns a formatted string of all errors in the stack
    };

    // These are shortcut functions for the above singleton:
    extern void PushError(const char * fmt, ...);
    extern void PushError(const std::string & str);

    #endif // ErrorStack_H

    // ErrorStack.cpp

    ///////////////////////////////////////////////////////////////////////////////
    // Class body for ErrorStack
    ///////////////////////////////////////////////////////////////////////////////

    #include "ErrorStack.h"

    #include <stdio.h>

    using namespace std;

    ///////////////////////////////////////////////////////////////////////////////
    // Clear:
    // Empties the error stack.
    // Needs calling if you attempt to continue from an error, which may include
    // anywhere you choose to completely ignore one.
    ///////////////////////////////////////////////////////////////////////////////
    void ErrorStack::Clear()
    {
        Stack.clear();
    }

    ///////////////////////////////////////////////////////////////////////////////
    // Push:
    // Adds an error message to the stack.
    ///////////////////////////////////////////////////////////////////////////////
    void ErrorStack::Push(const std::string & str)
    {
        Stack.push_back(str);
    }

    ///////////////////////////////////////////////////////////////////////////////
    // Push:
    // Adds an error message to the stack.
    ///////////////////////////////////////////////////////////////////////////////
    void ErrorStack::Push(const char *fmt, ...)
    {
        static char str[8192];

        va_list argptr;
        va_start(argptr, fmt);
        vsprintf(str, fmt, argptr);
        va_end(argptr);

        Stack.push_back(str);
    }

    ///////////////////////////////////////////////////////////////////////////////
    // Report:
    // Returns the errors on the stack as a single formatted string.
    ///////////////////////////////////////////////////////////////////////////////
    string ErrorStack::Report()
    {
        string str;
        vector<string>::iterator s;

        if(Stack.size())
        {
            s = Stack.begin();
            str = *s++;
            while(s != Stack.end())
            {
                str = *s + " [" + str + "]";
                ++s;
            }
        }
        else
        {
            str = "No Error";
        }

        return(str);
    }

    ///////////////////////////////////////////////////////////////////////////////
    // The following functions are global, and exist to simplify the use of the
    // error stack:
    ///////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////
    // PushError:
    // Pushes a string onto the error stack.
    ///////////////////////////////////////////////////////////////////////////////
    void PushError(const string & str)
    {
        ErrorStack::Instance()->Push(str);
    }

    ///////////////////////////////////////////////////////////////////////////////
    // PushError:
    // Pushes a string, which can include formatting, onto the error stack.
    ///////////////////////////////////////////////////////////////////////////////
    void PushError(const char *fmt, ...)
    {
        static char str[8192];

        va_list argptr;
        va_start(argptr, fmt);
        vsprintf(str, fmt, argptr);
        va_end(argptr);

        ErrorStack::Instance()->Push(str);
    }

    // Example code

    #include <stdio.h>

    #include "First.h"
    #include "ErrorStack.h"

    int main(int argc, char **argv)
    {
        First first;

        if(!(first.DoSomething()))
        {
            PushError("Do something failed");
            printf("%s\n", ErrorStack::Instance()->Report().c_str());
            exit(0);
        }
        return(0);
    }

    #include "Second.h"

    class First
    {
        protected:
            Second second;
        public:
            bool DoSomething();
    };

    #include "First.h"
    #include "ErrorStack.h"

    bool First::DoSomething()
    {
        if(second.DoSomethingElse())
        {
            return(true);
        }
        else
        {
            PushError("Failed to do something else");
            return(false);
        }
    }

    class Second
    {
        public:
            bool DoSomethingElse();
    };

    #include "Second.h"
    #include "ErrorStack.h"

    bool Second::DoSomethingElse()
    {
        // This always fails:
        PushError("This always fails");
        return(false);
    }

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.