Fake binary trees
... rants, ramblings and occasional good idea ...

C#-like events in C++

Since most of my daily work is done in C++,  every now and then I miss some niceties which are available in C#. One such example are .NET events, which are really an elegant solution for implementing observer design pattern.

Idiomatic C++ implementations of observer pattern are based on defining abstract interfaces, which have to be implemented by subject and observer classes. Sometimes it would be nicer to bind to an event just based on event/method signature instead of building all the needed scaffolding.

Here is a quick take at this problem. This is by no means a complete solution, but can be a basis for more elaborate one.

Events are defined and fired like this:

class Subject
{
public:
    event<int> ValueChanged; // same template is used, regardless of number of arguments
    event<int, const char*> ValueAndNameChanged;  // no need for having event1<A> and event2<A, B>

    void SetValue(int x)
    {
        // .. do something
        ValueChanged(x); // then fire ValueChanged event
    }

    void SetValueAndName(int x, const string& name)
    {
        // do something...
        ValueAndNameChanged(x, name.c_str()); // fire ValueAndNameChanged event
    }
};

Clients consume events like this:

// implementation of observer object
class Observer
{
public:
    void OnValueChanged(int i)
    {
        printf(" - non-static handler for SetValue called, new value = %ld\n", i);
    }

    static void OnValueChangedStatic(int i)
    {
        printf(" - static handler for SetValue called, new value = %ld\n", i);
    }
};

// global functions work, too:
void OnValueChangedGlobal(int i)
{
    printf(" - global handler for SetValue called, new value = %ld\n", i);
}

// test
int _tmain(int argc, _TCHAR* argv[])
{
    Subject subject;
    Observer observer;

    subject.ValueChanged += Observer::OnValueChangedStatic; // static member method as handler
    subject.ValueChanged += OnValueChangedGlobal; // global function as handler
    subject.ValueChanged += event_handler(&observer, &Observer::OnValueChanged); // non-static member

    subject.SetValue(3); // this will fire the event

    // detach from event. This is not necessary when lifetime of observer is shorter than lifetime
    // of the subject, but if subject outlives observer, handler must be unregistered
    subject.ValueChanged -= Observer::OnValueChangedStatic;
    subject.ValueChanged -= OnValueChangedGlobal;
    subject.ValueChanged -= event_handler(&observer, &Observer::OnValueChanged);

    return 0;
}

Output of this snippet is:

d:\dev\events>events.exe
  - static handler for SetValue called, new value = 3
  - global handler for SetValue called, new value = 3
  - non-static handler for SetValue called, new value = 3

It is possible to use static and non-static members as handlers, as well as global functions. Note, however, that there is one difference compared to event behavior in .NET: if you register same handler twice, it will be called only once. This can easily be changed in the code, but it seems more logical to me.            

Attached archive contains a Visual Studio solution with implementation of event class (events.h) and some additional examples. 

events.zip (5.14 kb)

Posted at 16:12 on April 29, 2008
Categories: C++   E-mail | del.icio.us | Permalink | Comments (2) | Post RSSRSS comment feed

Automatic memory management myth

At a recent interview, a job candidate ticked me off when we reached one of the topics which is very dear to my heart. We are mostly C++ shop, but many of our job applicants come from .NET background. So during the interview we came to the topic of explicit memory management vs. automatic memory management (a.k.a. garbage collection), and the guy (with substantial C++ experience) started ranting about how troublesome explicit memory management is, with all this extra caution required to call delete for every new, as opposed to simplicity of platforms which support GC.

Well, if you happen to agree with this guy, then you are not doing it right :)

Although GC can be done in C++ (see here), that is not the point: the main issue here is that many people are still doing C code using C++ compiler.

Modern C++ is very different beast from C, and as such provides different patterns for common problems. The concept of smart pointers alleviates much of the manual work that is otherwise needed to handle allocations correctly. The STL comes with std::auto_ptr, which makes it trivial to ensure the proper deallocation in face of exceptions or normal scope exit. 

If you need to handle an array, STL is your friend again: there is std::vector, and it also takes care of transparent resizing/reallocation, so you don't have to worry about it.

What is (currently) lacking in STL is an implementation of smart pointer with shared semantics. However, there is plenty of such implementations, most notably the excellent boost::shared_ptr, which is a reference counted implementation that makes sure that the allocated object is deleted when the last reference to it goes away. It will also become a part of new C++ standard, and some vendors support it already.

One issue where GC (at least when implemented with mark-and-sweep algorithm, like in Java or .NET) has an advantage to explicit memory management is the problem of circular references, i.e. when two objects hold a reference to each other. This is really a problem for reference counted implementations, but most cases can be handled by judiciously using weak pointers, and such an implementation is provided by boost::weak_ptr, which will also be included in new C++ standard.

If you think that this problem is a 'deal breaker' to prefer platforms with GC, bear in mind that the similar problem also exists even there, and that there is a very good reason why Java and .NET both provide WeakReference class (e.g. see here for one such problem).

What I really find annoying is the fact that the only resource which is deemed to be important enough to be automatically handled is memory. For everything else, like database connections, kernel or GDI objects, clients have to explicitly call Dispose, and it seems that most developers on managed platforms have no problems with that. A coworker who recently switched from VB to .NET found out hard way that you really have to call Dispose() on your bitmaps. What happens when you have to share such an object between multiple clients (yes, I know that there are idioms to avoid this resource sharing)? Who calls Dispose? Can you be sure that it has not already been disposed? 

GC is a nice and useful thing, but it is not a silver bullet, and has its own problems.

Deterministic finalization is what makes it possible in C++ to treat all resources equally: whether it is a memory, a bitmap, a COM object or database reference, with a simple wrapper around it, you can rest assured that the object will be properly released as soon as it is not referenced anymore. Actually, smart pointers are only a special case of what is one of the most powerful concepts in C++, although a bit unfortunately named: RAII

If you are a C++ programmer, take some time to learn C++ idioms. It will make you a better programmer, and your code a better code.

When you program in C++, write C++, not C.

Posted at 14:32 on April 17, 2008
Categories: C++ | Software Design   E-mail | del.icio.us | Permalink | Comments (0) | Post RSSRSS comment feed