Hello,

I am having a particularly nasty case of "What is the syntax?" while working on an event driven library. Basically this is what I want:

class Object
{// private:
    public:
    virtual bool onEvent(Event e)=0;//all objects have to react to events
    Object operator|(const Object &o)
    {
        // I want to return an Object which will execute both onEvents...
        // How can I do this... IF I can do this...
        // my example attempt: (I have yet to fully memorize the lambda expression syntax in c++11)
        return Object::onEvent(Event e)=[]{return (*this).onEvent(e) || o.onEvent(e);}
    }
};
class Test1:public Object
{// private:
    public:
    virtual bool onEvent(Event e)
    {
        cout<<"Test1"<<endl;
        return 0;//do not delete
    }
};
class Test2:public Object
{// private:
    public:
    virtual bool onEvent(Event e)
    {
        cout<<"Test2"<<endl;
        return 0;//do not delete
    }
};
//a more complex test
class Incrementer:public Object
{// private:
    int x;
    public:
    Incrementer():x(0){}
    virtual bool onEvent(Event e)
    {
        cout<<"Counter is: "<<x<<endl;
        return 0;
    }
};

So that this code:

Test1 t1;
Test2 t2;
Incrementer i;
f(t1|t2|i);

Will pass an object to f() whose onEvent function consists of:

cout<<"Test1"<<endl;
cout<<"Test2"<<endl;
cout<<"Counter is: "<<x<<endl;

Is this even possible? If it is, what would its constraints be? I would think that Test1 and Test2 should be possible (since they could easily be implemented via an array of function pointers) but how will Incrementer have access to i if it is currently an Object?

Recommended Answers

All 7 Replies

After giving it more thought I realize that it is impossible as I initially presented it since I cannot create a brand new class in a function but rather can only create a new object. As such I think I will likely need an intermediate type which will manage function pointers to allow for | chaining? I am still iffy on the details as to how this can be done...

There are a few minor issues to begin with:

virtual bool onEvent(Event e)=0;
..
  const Object &o
..
  o.onEvent(e);

This is not going to compile because the member function onEvent is a non-const member, and the object o is a const object. You will need to choose between having a const onEvent member, as so:

virtual bool onEvent(Event e) const = 0;

Or requiring your objects to be non-const.

Second, there is really no point in defining operators to be member functions if you don't particularly need it's implementation to be very strongly tied to the class' internals. In this case, your operator| could easily be a non-member function. This will free up a few options to solve your problem, because you can make smart use of overloading (on both parameters).

Third, beyond the fact that you cannot really "reassign" the onEvent function to be a lambda expression (as your second post suggests you have already realized), there is the problem of the capture of objects into the lambda. Normally, you would have to have done something like this to declare your lambda:

[this, &o](Event e) {
  return this->onEvent(e) || o.onEvent(e);
}

which piece-by-piece means:
[this,..]: Capture the this pointer by value.
[.., &o]: Capture the o object by reference.
(Event e): Make a lamba that takes one parameter (an Event object by value).
With no return type specified, meaning, the compiler deduces it from the return statement (if trivial enough).

Now that the syntax is "valid" for creating the lambda, you should see one glaring issue: the two objects are captured by reference / pointer upon creation of the lambda. Captured arguments are carried around in the lambda like references in a reference_wrapper, i.e., as a raw pointer (that can be kept and copied easily). This means that creating lambda expressions like that that are "persistent" beyond the scope of the function would be an obvious memory management hazard because there is no way for you to really guarantee that those pointers will remain valid. This is not, by any means, acceptable in this particular context.

Another little observation too is the choice of the expression like this->onEvent(e) || o.onEvent(e). Are you sure you really want to use a short-circuit operator for this?

Finally, I would also say that the choice of the operator| might be a bit dubious. It has the right characteristics (low precedence and left-to-right associativity) to make it a good candidate for this application. However, it is quite rare to use that operator to mean "chain stuff together", and it is also a commutative operator (which would not really mesh well with the short-circuit operator you used to chain the event calls). But, that is for you to choose whether you really think it is appropriate. The more traditional alternative would be <<.

Is this even possible?

Of course it is, this is a very common and pretty standard "chaining" problem.

If it is, what would its constraints be?

That's where things get a bit more complicated. There are a number of alternative implementations with different constraints / trade-offs.

It all depends on what kind of objects you really have, and how you intend to use them.

In any case, you have more or less two choices for chaining. Imagine you have the following classes:

class Object { .. };

class Test1 : public Object { .. };
class Test2 : public Object { .. };
class Incrementer : public Object { .. };

Then, the first option is to have a composite class like this (in pseudo-C++):

class Composite : public Object {
  private:
    /*Object-ref/ptr*/ o1, o2;
  public:
    virtual bool onEvent(Event e) {
        return o1.onEvent(e) || o2.onEvent(e);
    };
};

Composite operator|(/*Object-ref/ptr*/ o1, 
                    /*Object-ref/ptr*/ o2) {
  return Composite(o1, o2);
};

Because the composite class can store any two objects, one of them, or both, could be a composite object too, meaning that you can chain as many objects as you wish. In effect, this constructs a binary linked-tree of objects.

The other option is to have a chain class, that just contains a list for objects. Like this (in pseudo-C++):

class ObjectChain : public Object {
  private:
    std::vector< /*Object-ref/ptr*/ > objects;
  public:
    virtual bool onEvent(Event e) {
      for(auto o : objects)
        if(o.onEvent(e))
          return true;
      return false;
    };

    friend
    ObjectChain& operator|(ObjectChain& o1, 
                           /*Object-ref/ptr*/ o2) {
      o1.objects.push_back(o2);
      return o1;
    };

    friend
    ObjectChain operator|(/*Object-ref/ptr*/ o1,
                           /*Object-ref/ptr*/ o2) {
      ObjectChain obj_chain;
      obj_chain.objects.push_back(o1);
      obj_chain.objects.push_back(o2);
      return obj_chain;
    };
};

Or something similar to that. The point being that you use overloading to detect if the first object is an object chain and then add the other object to the chain (notice: it does not really matter if the other object is a chain or not).

The two options are more or less equivalent. The only real difference is that the first pattern (composite object) is more common in generic programming because it is easier to turn that into a composite class template, like this:

template <typename First, typename Second>
class Composite {
  private:
    First  o1;
    Second o2;
  ...
};

// or, as a singly-linked lists:

template <typename Tail = /*dummy type*/ >
class Test1 { 
  private:
    Tail tail_obj;
  public:
    bool onEvent(Event e) {
      /* do Test1 code here */
      // then, forward the call to the tail-object:
      return tail_obj.onEvent(e);
    };
};

.. and similarly for all "event handling" classes.

But if you are not going to go for a template approach, then the two solutions are essentially equivalent. They are two classic mechanisms to "chain stuff together".

What really matters is how you are going to store the objects in the composite / chaining class. The code above just says /*Object-ref/ptr*/ or does some impractical return-by-value and pass-by-non-const-reference business. This is not at all viable, obviously.

One conventional option in C++ is to assume that callable objects (like event handlers) are just "stateless functors", or at least, very cheap to copy. In that case, you would just copy around the handlers. That is what is typically done in generic programming (with templates). But if you are using an object-oriented approach (with virtual functions and base-classes), then that becomes very tricky to realize, and I would not recommend trying to.

The other options are to use smart pointers. In this case, there are really only two options: std::shared_ptr and std::unique_ptr. Which one depends on what kind of "life-time" you want your objects to have. But you don't have to commit to one or the other if you use the first "composite" option:

template <typename Ptr1, typename Ptr2 = Ptr1>
class Composite : public Object {
  private:
    Ptr1 o1;
    Ptr2 o2;
  public:
    Composite(Ptr1&& aO1, Ptr2&& aO2) : 
              o1(std::move(aO1)),
              o2(std::move(aO2)) { };

    virtual bool onEvent(Event e) {
        return o1->onEvent(e) || o2->onEvent(e);
    };
};

std::unique_ptr<Object> operator|(std::unique_ptr<Object>&& o1, 
                                  std::unique_ptr<Object>&& o2) {
  return std::unique_ptr<Object>(
    new Composite< std::unique_ptr<Object> >(
      std::move(o1), std::move(o2)
    )
  );
};

std::unique_ptr<Object> operator|(std::unique_ptr<Object>&& o1, 
                                  std::shared_ptr<Object> o2) {
  return std::unique_ptr<Object>(
    new Composite< std::unique_ptr<Object>, std::shared_ptr<Object> >(
      std::move(o1), std::move(o2)
    )
  );
};

std::unique_ptr<Object> operator|(std::shared_ptr<Object> o1, 
                                  std::unique_ptr<Object>&& o2) {
  return std::unique_ptr<Object>(
    new Composite< std::shared_ptr<Object>, std::unique_ptr<Object> >(
      std::move(o1), std::move(o2)
    )
  );
};

std::unique_ptr<Object> operator|(std::shared_ptr<Object> o1, 
                                  std::shared_ptr<Object> o2) {
  return std::unique_ptr<Object>(
    new Composite< std::shared_ptr<Object> >(
      std::move(o1), std::move(o2)
    )
  );
};

This should basically solve all your problems, as long as you use either unique-pointers or shared-pointers to hold your handlers.

There is another option that does not rely on smart pointers and still conserve dynamic polymorphism. That method would rely on a similar business as captured lambda arguments. In other words, it would rely on grabbing objects by non-const reference and storing them by value within a reference-wrapper. Obviously, the issue here is that this does not at all solve the memory management issue, and because of that, it's use would have to be extremely limited, which I don't think is appropriate for this kind of "event-driven" system.

I hope this gives you enough food for thoughts.

Wow, thank you. I honestly did not think this would be such an interesting topic. Anyways, I have yet to make myself comfortable with the C++11 standard (even though I know I should) so I tried to implement using standard unsafe pointers and ugly references, since I do not yet fully understand how the C++11 safe pointers work. I can see how some kind of auto-managed pointer would be very helpful here, so I will try to understand them better once I get the general idea correct.

I got it to compile, but the output is not what I expected. Basically here is my test code:

#include <iostream>
#include <vector>
using namespace std; //its just a test code-segment
class Object
{// private:
    public:
    virtual bool onEvent()=0;
};
class test:public Object
{// private:
    public:
    virtual bool onEvent(){cout<<"Test"<<endl;return 0;}
};
class inc:public Object
{//private:
    int x;
    public:
    virtual bool onEvent(){cout<<"X="<<x++<<endl;return 0;}
};
class Cumulator:public Object
{// private:
    Object &a,&b;
    public:
    Cumulator(Object &a, Object &b):a(a),b(b){}
    virtual bool onEvent(){return a.onEvent()|b.onEvent();}//no more short-circuit
};
//when I put const Object & before it was just a force of habit
Cumulator operator|(Object &a,Object &b)
{
    return Cumulator(a,b);
}

int main() {
    vector<Object*> objs;
    test t;
    inc i;
    objs.push_back(&t);
    objs.push_back(&i);
    Object *o;
    *o=t|i;
    objs.push_back(o);
    for (int i=0; i<10; ++i){
        for (auto x:objs)
        {
            x->onEvent();
        }
    }
    return 0;
}

I would expect that this would output would be:

Test
X=1
Test
X=2
Test
...

Or possibly:

Test
X=1
X=2
Test
Test
X=3
...

However when I compile it I end up with the unexpected:

Test
X=1
X=2
Test
X=3
X=4
...

What did I do wrong? (aside from using complicated and confusing pointers)

Here is the output that I get:

Test
X=65535
Segmentation fault (core dumped)

Which did not surprise me at all. I was more surprised that my compiler actually accepted the code, because I did not expect the line *o=t|i; to compile successfully (because it would seem to require copying an abstract-class object).

I can easily fix the code to get well-defined behavior (not a seg-fault or other corruption problem) (and I also could not resist fixing the bad formatting!):

#include <iostream>
#include <vector>

using namespace std; //its just a test code-segment

class Object {
  public:
    virtual bool onEvent() = 0;

    // NEVER FORGET TO HAVE A VIRTUAL DESTRUCTOR!!
    virtual ~Object() { };
};

class test : public Object {
  public:
    virtual bool onEvent() {
      cout << "Test" << endl; 
      return false;
    };
};

class inc : public Object { 
  private:
    int x;

  public:
    inc() : x(0) { };

    virtual bool onEvent() {
      cout << "X=" << x++ << endl;
      return false;
    };
};

class Cumulator:public Object {
  private:
    Object &a,&b;
  public:

    Cumulator(Object &a, Object &b) : a(a), b(b) { };

    virtual bool onEvent() {
      bool ra = a.onEvent();
      bool rb = b.onEvent();
      return ra || rb;
    };
};

Cumulator operator| (Object &a, Object &b)
{
    return Cumulator(a,b);
}

int main() {
  vector<Object*> objs;
  test t;
  inc i;
  objs.push_back(&t);
  objs.push_back(&i);
  Cumulator c = t | i;
  objs.push_back(&c);
  for (int i=0; i<10; ++i){
    for (auto x:objs)
    {
      x->onEvent();
    }
  }
  return 0;
}

Which did indeed get the exact expected output:

Test
X=0
Test
X=1
Test
X=2
Test
X=3
Test
...

If you examine the code, you'll see a few key fixes that I made. The first, of course, is to declare a virtual destructor in the base-class, never forget to do that! (although, in this case, it did not really matter, but in general, it will matter a lot).

The second is that I initialized the x data member of the inc class to 0 upon construction. When you don't have a constructor defined, and you don't give an initial value to data members, they will be left uninitialized, which explains the "65535" value I got on the initial output.

The third thing was that I eliminated this business of having a Object* o; *o=t|b; because it had undefined behavior. I suspect you are using the Microsoft compiler, because I believe it is the only compiler that would allow such monstrosity to kind of work somehow (through its very permissive extensions). The point is, that code creates an uninitialized pointer, and thus, a pointer that points nowhere in particular (any random address). Then, it accesses the memory at that random address, and overwrites it with a sliced version of the temporary Cumulator object that is returned by the function. And then, after that, this object will be used. Every single one of these results in at least a memory corruption, usually a segmentation fault (or access violation), or worse. This explains the segmentation fault I got in my initial run of the code, which, as I said, was something I totally expected would happen.

The corrected version that I posted is "correct" in the sense that it has well-defined behavior and produces the desired output. However, one thing that should be obvious is that it's not a very convenient method (you have to create concrete-class objects, and manually ensure their life-times are correctly laid out). For example, if you were to do this:

vector<Object*> getEventHandlers() {
  vector<Object*> objs;
  test t;
  inc i;
  objs.push_back(&t);
  objs.push_back(&i);
  Cumulator c = t | i;
  objs.push_back(&c);
  return objs;
}

It would be an error because the objects referred to by the pointers within objs will be destroyed as soon as you return from the function, meaning that that vector is completely corrupt from that point on, and any further operations with those object pointers would result in undefined behavior (most likely, memory corruption or seg-fault).

In other words, this method is only good for short-lived (within one function-body) purposes. But it is one of the valid methods, albeit a highly constraining one.

aside from using complicated and confusing pointers

Pointers are neither complicated nor confusing, they are unsafe. They are not complicated, a pointer is nothing more than a simple variable whose value is an address in memory (i.e., an integer number, that's all). They are not confusing: are int variables confusing to you? A pointer is essentially just an int, an integer number. Pointers are unsafe because they are so simple, i.e., they don't have behavior attached to them (like smart-pointers do), they have undefined ownership semantics, and of course, the number itself could be wrong, and there is usually no way to tell. That's why pointers should always be wrapped inside an interface (class) that does carry behavior, ownership and control over the validity of the pointer.

Pointers: beginners hate them; experts hide them.

I can see how some kind of auto-managed pointer would be very helpful here, so I will try to understand them better once I get the general idea correct.

This tutorial should give you a pretty good start. I urge you to get up to speed on using smart-pointers and caring more deeply about ownership design, because they are core aspects of programming (in any language) and will save you from a tremendous amount of trouble in the future.

Perfect, as usual! I just want to double-check that I fully understand.

Ideally I would want to do something like this:

Test t;
Incrementer i;
Object o=t|i;
o.onEvent();//fails, even if Objects are instantiable since I cannot access the derived functions in the Accumulating object

Since I can't access derived functions from an Object this will not work, leaving me with 3 options:

Option 1: raw pointers

Test t;
Incrementer i;
Object *o;
Cumulator c=t|i;
o=&c;
o->onEvent();//Yay!

Pros: Valid in old C++. Easy to implement.
Cons: Leads to complicated code (this is what I meant by pointers being complicated, they themselves aren't but they lead to complicated code). User must manage objects intelligently to prevent the "pointee"s from going out of scope.

Option 2: smart pointers

//Please excuse any minor lapses in syntax
Test t;
Incrementer i;
unique_ptr<Object> o(&t/&i);//perhaps I can define '/' as pointer version of |
o->onEvent();//Yay!

Pros: Pointer-safe (User can't 'break' the pointers). Easy to use. Can be placed in vectors.
Cons: Only valid in C++11. Harder to implement (requires extensive knowledge of smart pointers)

Option 3: Instantiable Objects

Test t;
Incrementer i;
(t|i).onEvent();//Yay?

Pros: Pointer-free. Easy to use.
Cons: Ridiculous to implement. My first attempt:

class Object
{// private:
    bool (*onE)();//function pointer to onE (I think this has to be a unique_ptr
    public:
    Object()
    {
        onE=[](){return false;};
    }
    virtual bool onEvent()
    {
        return onE();
    }
    Object &operator|(Object &o)
    {
        bool (*tmpE)()=onE;
        onE=[]()
            {//I don't know if these work once the pointers/objects go out of scope?
                bool a=tmpE();//(a)
                bool b=o.onEvent();
                return a||b;
            };
        delete tmpE;//unless I need to hold onto it for (a)
        return *this;
    }
    virtual ~Object()
    {
        //clean up onE?
    }
};
class Test:public Object
{// private:
    int x;
    public:
    Test():x(0){}
    Test(int i):x(i){}
    virtual bool onEvent()
    {
        cout<<"Test "<<x<<endl;
        return false;
    }
};
class Test2:public Object
{// private:
    public:
    virtual bool onEvent()
    {
        cout<<"Hello World!"<<endl;
        return false;
    }
};

//Elsewhere:
Test t;
Test t1(1);
Test2 t2;
(t|t1|t2).onEvent();//Does this... work?

It would seem like this should be implementable... somehow? If so then it would of course be the ideal solution. Is it possible to implement this?

PS: I read your tutorial on smart pointers. They seem extremely useful. From what I understand they basically turn pointers into normal objects, so you can create a unique_ptr<Base> type and use it in all polymorphic situations to give access to the derived classes? Very useful! However when creating portable code I like to avoid C++11 in my interfaces. (I still use the new features within the code implementation, just not in the declaration)

I think that your implementation of the Option 3 is quite messed up. There are a number of impossible things in there, and errors. This part is especially troublesome:

    onE=[]() {
            bool a=tmpE();//(a)
            bool b=o.onEvent();
            return a||b;
        };
    delete tmpE;

1) Your lambda expression here is a stateful lambda, because it needs to capture o and tmpE. Stateful lambdas cannot be converted to function pointers. You need to use std::function<bool()> instead of a bool(*)() function-pointer.
2) Lambda expressions are not allocated with new, and thus, should not and cannot be deallocated with delete. Lambdas are syntactic sugar for function objects, i.e., they are copied around by value. They are not allocated on the heap, they are created like any other local variable (and can be copied into the return value or a data member). Under some conditions, lambdas can be converted to function-pointers, but that is only if the "call operator" in the equivalent function-object class could be implemented as a static member function (i.e., stateless, or no captured parameters).

And I don't think this option really works very well. You are kind of getting the worst of both worlds.

I think that we are starting to talk in circles a bit with all these options, because we lack a clear set of requirements. There are different solutions for different sets of requirements. Initially, I assumed you had some sort of traditional OOP requirements (run-time polymorphism, shared ownership, persistent instances, etc.). But from the options you laid out, there doesn't really to be any clear requirements that stand out. That's what I meant earlier by saying that "That's where things get a bit more complicated.", when referring to the constraints of the different solutions. The thing is, there is probably about a dozen different ways to implement this kind of thing, and there is no one-size-fits-all solution, the choice is highly dependent on what you really want to have as a behavior. I think this is the real problem here, not the technicalities of how to do it.

However when creating portable code I like to avoid C++11 in my interfaces.

That's fine, most of the C++11 features are not really necessary and can be circumvented, including unique_ptr. It's just not as convenient.

we lack a clear set of requirements.

I think that is exactly the problem, when I first posted this thread I did not have a clear set of requirements, but now I do. I tend to start making my supporting classes/headers/libraries/etc by first deciding how I want the resulting use of the code to look like. Once I know what I want the result to be I try to create an interface that meets those requirements.

Here is my sample:
(Note that for obvious reasons it is untested [I haven't created the header yet])

#include "myHeader.h"
using namespace EG;//just to let you know that everything in myHeader is hiding in a namespace
class Player:public Object
{// private:
    int x,y;
    public:
    Player():x(0),y(0){}
    Player(int x, int y):x(x),y(y){}
    Reaction onKeyPress(Key k)
    {
        switch (k)
        {
            case ESCAPE:
            return FULL_EXIT;
            case SPACE:
            return DELETE_OBJECT;
            default:
            return Object::onKeyPress(k);
        }
    }
    Reaction onStep(const InputState &is)
    {
        if (is.getKey(UP_ARROW))
            y--;
        if (is.getKey(DOWN_ARROW))
            y++;
        if (is.getKey(LEFT_ARROW))
            x--;
        if (is.getKey(RIGHT_ARROW))
            x++;
        return Object::onStep(is);
    }
};
class Audio:public Object
{// private:
    bool muted;
    int volume;
    public:
    Audio():muted(false),volume(10){}
    Reaction onKeyPress(Key k)
    {
        switch (k)
        {
            case 'M':
            muted=!muted;
            return 0;//success return value
            case PLUS:
            volume++;
            return 0;
            case MINUS:
            volume--;
            return 0;
        }
    }
}
class HUD:public Object
{// private:
    int mx,my;
    Sprite cur;
    public:
    //scene is a protected member of Object, a pointer to the handler of the objects
    HUD():mx(0):my(0):cur("myCursor.png"){}
    Reaction onAdd(const InputState &is)
    {
        if (!scene) return NO_SCENE;
        mx=is.mouse.x;
        my=is.mouse.y;
        return Object::onAdd(is);
    }
    Reaction onMouseMove(int newx, int newy)
    {
        mx=newx;
        my=newy;
        return Object::onMouseMove(newx,newy);
    }
    void onDraw()
    {
        if (!scene) return NO_SCENE;
        scene->render(cur,mx-(cur.width/2),my+(cur.height/2));
    }
}
int main()
{
    Scene s;
    Player p;
    s.add(p);
    Audio a;
    HUD h;
    Object controllers;//will hold an object that does the job of both audio and HUD
    controllers=a|h;//or a&h or a+h or whatever! 
    s.add(a|h);//I just like the look of flags or'd together in functions C:
    //example of using that syntax to create 2 players with different starting positions
    //  They will however act entirely in unison, even being deleted together.
    s.add(Player(-100,0)|Player(100,0));
    //an example use would be to create a mob of objects that act identically but are
    //  dispersed and have them act as one (BossSegment(-100,0)|BossSegment(100,0))
    s.run();
    return 0;
}

While the use of my | operator would be rare, it could still be very helpful and thus I would like to have it in my program. The tough part is getting it to work.

After thinking about it for awhile I decided that it would just be easiest to make Objects non-abstract (what is the word?) and allow them to be created. Unfortunately this trivializes the problem (since I can use references effectively). This is unfortunate because I would love to learn more about proper practices in C++. Is there a quick way to find all of your tutorials? I noticed that most 'best practices' concern only object oriented programming paradigms, where can I find 'best practices' for other paradigms?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.