views:

65

answers:

1

Hi, I would like to use a generic error msg handler so I can GetLastError and SetError easily for any class. I came up with this scheme. But I have a couple of questions. Please note this implementation is just for testing. But I want to get the basic design right.

#include <iostream>
#include <stdarg.h>

class ErrorHandler
{
    public:
        virtual void SetError(const char* zFormat, ...)
        {
            va_list args;
            va_start (args, zFormat);
            vsnprintf (z_ErrorBuf, sz_MaxBufSize, zFormat, args);
            va_end (args);
        }

        const char* GetError() const
        {
            return z_ErrorBuf;
        }

        virtual ~ErrorHandler(){}

        explicit ErrorHandler(const size_t szMaxBufSize = 1024)
        {
            z_ErrorBuf = malloc(sizeof(char)*szMaxBufSize);
            sz_MaxBufSize = szMaxBufSize;
        }

        void ResizeBuffer(const size_t szMaxBufSize)
        {
            z_ErrorBuf = realloc(z_ErrorBuf, szMaxBufSize);
            sz_MaxBufSize = szMaxBufSize;
        }

    protected:
        char* z_ErrorBuf;
        size_t sz_MaxBufSize;
};

class MyClass;

//Worker can be just an interface if needed. So, can't use friend.
class Worker
{
    public:
        void Work(MyClass& oGod);
};

void Worker::Work(MyClass& oGod)
{
    //Work
    //OnError
    oGod.GetErrorHandler().SetError("Save me %s", "not");
}

class RecordingErrors
{
    public:
        const char* GetLastError() const
        {
            return oErrorHandler.GetError();
        }

        //GetErrorHandler is public
        ErrorHandler& GetErrorHandler()
        {
            return oErrorHandler;
        }

    private:
        ErrorHandler oErrorHandler;
};

class MyClass : public RecordingErrors
{
    public:
        bool GetThingsDone(Worker& me)
        {
            me.Work(*this);

            //on Error
            return false;
        }
};

int main()
{
    MyClass oL;
    Worker w;
    if(!oL.GetThingsDone(w))
    {
        std::cout << oL.GetLastError() << std::endl;
    }
}
  1. Can I override this function in a child class? virtual void SetError(const char* zFormat, ...).
  2. Can I do away with the RecordingErrors class? I feel that then MyClass would have to inherit from ErrorHandler which I don't think is good. Am I right or wrong? Is this another question about composition over inheritence. EDIT: I don't mean the name, but the idea?
  3. Any possible scenarios this approach would fail?
  4. is there a better way to implement error logging?(logging is not the right word here. What is it)
+2  A: 

Error Handler and logger are two different entities. Error handler decides what to do with the errors. Should it be send to logger immediately, should it be saved in databases, or should it be simply saved in some buffer till someone asks.
Logger decides how to log the given message. Should it be shown on the console or should it be saved in the disk file, in what format it should be shown.

Keep in mind the features of the logger.
1) It should be independent class. It's behavior should not depend on the other classes.
2) Logger most preferable should be singleton. You don't need many objects floating around doing the same thing. But then singleton classes has their own headaches when come to multi-threading. So I know this point is debatable.
3) It must and must have capabilities of asynchronous logging. This means producer and consumer implementation. (Logging is an i/o operation and hence expensive in nature. You don't want your main processing hogging for this. But then again you may not want to use threads to scrap this one.)

In you implementation of error logger I can't see any logger. Also your error handler is saving a single error. You may need the vector of errors. Keep SetError with fixed arguments. Pass arguments like error id, error message and error buffer length. Let the caller create the error message.

Manoj R
Not relevant to my question :( though your pointers are good. As I mentioned in the question, I couldn't think of the correct wording. And I just want the last error. Like `getlasterror()`
nakiya
I finally realized you are right. :). My problem was I tried to handle error handling and error logging both at the same time.
nakiya
@Manoj: +1 very useful information. Call stack may also be useful, and sometimes it's required by the project. With asynchronous logging (buffering enabled), it has to decide whether to flush the messages to the I/O. Without flushing, a critical error followed by a crash may leave no message in the log.
rwong