3

EDIT Answered: Although my original question didn't explain my needs in exactly the way the answer provided by Konrad Rudolph addressed them, He (by accident or design) essentially wrote for me what I was trying to write! The class itself does not get extended, but has it's functionality extended by making the class aware of new functions which allow it (the class) to handle a wider array of issues. My grateful thanks for an excellent answer which in turn is also a great tutorial, even if it does require some ahemm light? reading of books by me ;-).

I am sure that this a very basic question, but I am trying to self teach C++, and the reference books which I am using are providing answers which I do not understand.

For my project I am defining a class error_handler {} which processes exception messages and error numbers from the programs various points. It will log the error to a file and/or print the message to the screen using various member functions.

My goal is to create this so that it has it's own .h and .cpp files, and if I later need to extend it simply add a a new member function to handle some more obscure type of error manipulation.

My question then is this:

Since in error.h I have the code:

class error_handler
{
  // ctor dtor copy logger screen functions defined
}

And in error_handler.cpp I complete those definitions,

How can I simply simply add a new function to the class? I do not wish to sub class it, simply extend it.

For a use case scenario assume that error_handler is a class defined in a proprietary package where I am not free to modify the source directly, but am allowed to extend it in separate code.

EDIT: Answers and comments thus far seem to indicate I am trying to do something the language isn't meant to do. If so then so be it, I have learned something here. In the hopes that this is not the case I will leave the question open for a bit to see what else might wander onto this post......

Jase
  • 519
  • 1
  • 9
  • 23
  • 2
    The usual (and essentially only) way to extend a class is to "sub class" it (i.e., derive from it). "I do not wish to sub class it, simply extend it." sounds a little like "I want to swim without getting wet." – Jerry Coffin Jan 08 '14 at 15:11
  • @JerryCoffin Well there are some dirty macro hacks used in some (quite mature and professional) projects. However, probably unless you really know what your are doing you should use derivation/inheritance mechanism, especially on entry level. – luk32 Jan 08 '14 at 15:14
  • 1
    @NeonGlow I can think of **at least** three ways without inheritance. And in fact my favourite choice here would probably *not* be inheritance. – Konrad Rudolph Jan 08 '14 at 15:14
  • 1
    @Jerry Coffin -- LOL ... not afraid of getting wet, just inquiring about 'is it possible'. Other languages I have dabbled in have an `extends` modifier for class definitions. I was Hoping for something similar here. – Jase Jan 08 '14 at 15:23
  • @luk32 -- I am definitely `entry level` as you put it. So macro hacks are still in my future. – Jase Jan 08 '14 at 15:24
  • @KonradRudolph -- Without getting into macros, any chance you could post a simple sample? – Jase Jan 08 '14 at 15:25

3 Answers3

2

In C++ you cannot modify a class without modifying the class block and adding your function definition (i.e. in error.h)

If you cannot modify the header file, then your only options are to either inherit the class, or write non-member functions that operate on the class (but those will only be able to see public members)

benjymous
  • 2,102
  • 1
  • 14
  • 21
  • grrr... that is what I was afraid of. Explains why no book referenced the concept. – Jase Jan 08 '14 at 15:20
  • 1
    @Jase: This makes sense, too. You'd be able to cause all sorts of havoc if you could arbitrarily re-write types from different translation units! – Lightness Races in Orbit Jan 08 '14 at 15:32
  • @LightnessRacesinOrbit -- LOL -- Yes it would! I agree with (my loose understanding) the reason why not possible. It would create all sorts of problems. But with knowledge is power, and I am seeking to understand the language, not force it into new and obscure shapes and directions. Thanks for re-pointing this out to me. It was mentioned in the books, but I failed to make the connection till now. – Jase Jan 08 '14 at 15:36
  • @LightnessRacesinOrbit That’s called monkey patching and is a common concept in other languages. – Konrad Rudolph Jan 10 '14 at 15:43
  • @Konrad: Just one example of a reason that I do not use those languages. :) – Lightness Races in Orbit Jan 10 '14 at 16:24
  • @LightnessRacesinOrbit I should have added “… and, done correctly, it doesn’t pose a problem at all, and can be a very productive tool.” – Konrad Rudolph Jan 10 '14 at 17:31
2

It largely depends on what these functions are. Given your application, I’ll go ahead and assume that the functions you want to add are there to handle additional error types.

To keep it simple, let’s assume that each function (let’s call it handle) simply gets passed an error object which it analyses and either handles, or doesn’t handle. It returns a bool to indicate this.

Then you could have the following (simplified) error_handler class:

class error_handler {
public:
    using handler_t = bool (*)(error const&);
    std::vector<handler_t> handlers;

    // …

    void add_handler(handler_t handler) {
        handlers.push_back(handler);
    }

    void handle_error(error const& error) {
        for (auto const& handler : handlers)
            if (handler(error))
                break;
    }
};

This is assuming that your errors are represented by an instance of class error (and that you’re using C++11 – the syntax changes slightly otherwise).

Now, this class definition is fixed. You can add other handlers, defined in other files, simply by calling add_handler`. Here are two examples:

// Simple logging “handler”. Needs to be added first!

extern error_handler global_error_handler;

bool log_handler(error const& error) {
    std::cerr << error.message() << '\n';
    return false; // We only log, we don’t handle.
}

global_error_handler.add_handler(log_handler);
// Handler for missing dog food

extern error_handler global_error_handler;

errno_t const MISSING_DOG_FOOD = 42;

bool dog_food_missing(error const& error) {
    if (error.code() != MISSING_DOG_FOOD)
        return false;

    global_dog_food_container.add(some_food());
    return true;
}

global_error_handler.add_handler(dog_food_missing);
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • WOW!! Far more of an example than I was expecting! You have added some functionality with which I am unfamiliar so I am doing some more reading. (Specifically `using handler_t = bool (*)(error const&);` where I am haven't seen `using` in that context) Thank you for the wonderfully detailed example! – Jase Jan 08 '14 at 16:33
  • Being new to C++11 I have a couple of questions about this code I still cant seem to sort out (although it works wonderfully). I don't want to clutter SO with this would you be kind enough to contact me via the email in my profile page? I would really appreciate it. – Jase Jan 09 '14 at 17:10
  • Is this the `chain of command` pattern? And I'm curious, what are the other two ways you mentioned? – Adri C.S. Jan 10 '14 at 09:55
  • 1
    @AdriC.S. I have to admit that I don’t know that pattern. The other ways are wrapping the class inside another ([decorator pattern](https://en.wikipedia.org/wiki/Decorator_pattern)) which, in a way, is the “canonical” solution to this problem, or a template-based solution, where behaviour is added via policies rather than (as in my above code) via callbacks. Note that the decorator solution *does* use inheritance – but it’s not inheriting from OP’s class but rather from a common base. – Konrad Rudolph Jan 11 '14 at 11:04
  • @Jase Sorry, I was – and still am – rather busy, and currently abroad. However, feel free to create a [chat room](http://chat.stackoverflow.com/) for us both and ask your questions there. Drop a link here in the comments, then I can reply later. – Konrad Rudolph Jan 11 '14 at 11:05
  • @KonradRudolph Thanks for the info. I used the wrong name, it's "chain of responsibility". I first saw it in the plugin mechanism of `OpenSceneGraph`. – Adri C.S. Jan 11 '14 at 21:31
1

The only way to extend a class without modifying its original definition is by creating a new class derived from it. Perhaps you should be more specific with what you're trying to do. Does the existing code that uses error_handler have to be affected by your changes? For example, if you simply want to add a function that will log the error in a different way, and only intend to use that function in your new code, you could make a normal, non-member function that accepts a reference error_handler, but a cleaner way would be to create a my_error_handler (derived) class with the new method. An pointer/reference to an instance of my_error_handler can be freely converted to a pointer/reference to error_handler if you wish to pass it to existing code.

riv
  • 6,846
  • 2
  • 34
  • 63
  • Not sure how to go about converting the pointer/reference yet. I am (perhaps foolishly) jumping around a bit in the books in an attempt to rewrite very old code as a way of learning new concepts. (Original code was C=64 basic) – Jase Jan 08 '14 at 15:31