How do events actually work?

As a C# programmer I know about events. Creating delegates and so forth. But how do events actually work? What I mean is the actual underlying mechanism. So when you click your mouse on a file, what actually happens? It is the same with graphics, it is like magic. I imagine it is a bunch of code that references memory in C/C++. I know in today's world of programming nobody really thinks about this, but what would C/C++ code look like with no API/includes. Just raw code to determine an event. Or in other words, when the mouse was first invented and it took code to trigger an event, of clicking a folder/file. What would that C/C++ code look like?
Hi,

There are things called Design Patterns - they are a very valuable tool.

For events there is this:
http://www.marco.panizza.name/dispenseTM/slides/exerc/eventNotifier/eventNotifier.html

There are many Design Patterns and combinations of them - Google it :+)
> But how do events actually work? What I mean is the actual underlying mechanism.

Some component detects that something of interest has occurred (eg. the component that handles mouse input detects that a button has been clicked) and then calls a function to notify other components which have expressed an interest in the event.


> but what would C/C++ code look like with no API/includes. Just raw code to determine an event.

As an example, if the event of interest is that the value of an integer member has changed, something like this (no includes other than <iostream>; that is there only so that we can see that events are being delivered):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

struct event
{
    using event_type = void(int) ;

    event& operator += ( event_type* fn )
    {
        for( auto& p : call_backs ) if( p == nullptr ) { p = fn ; break ; }
        return *this ;
    }

    void operator() ( int v ) const
    { for( auto& p : call_backs ) if(p) p(v) ; else break ; }

    static constexpr std::size_t MAX_LISTENERS = 128 ;
    event_type* call_backs[MAX_LISTENERS] {};
};

struct A
{
    int value = 0 ;

    void update( int delta ) { value += delta ; value_changed(value) ; }

    event value_changed ;
};


void event_handler_one( int arg ) { std::cout << "::event_handler_one(" << arg << ")\n" ; }
void event_handler_two( int arg ) { std::cout << "::event_handler_two(" << arg << ")\n" ; }

int main()
{
    A a ;

    a.value_changed += event_handler_one ;
    a.update(43) ;

    std::cout << "----------------\n" ;
    a.value_changed += event_handler_two ;
    a.update(57) ;
}

http://coliru.stacked-crooked.com/a/1905b5a062c76ca0

This would be the outline of somewhat more typical C++ event handling code (except that, for brevity and clarity, perfect forwarding has been elided):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <functional>
#include <vector>

template < typename CALLABLE > struct event
{
    template < typename FN > event& operator += ( FN&& fn )
    { call_backs.emplace_back(fn) ; return *this ; }

    template < typename... ARGS > void operator() ( ARGS&&... args ) const
    { for( auto&& fn : call_backs ) fn(args...) ; }

    std::vector< std::function<CALLABLE> > call_backs ;
};

struct A
{
    int value = 0 ;

    void update( int delta ) { value += delta ; value_changed( this, value ) ; }

    event< void( const A*, int) > value_changed ;
};

void event_handler( const A* sender, long arg )
{ std::cout << "::event_handler(" << arg << ")  sender: " << sender << '\n' ; }

int main()
{
    A a ;
    std::cout << "address of a is " << std::addressof(a) << "\n----------------\n" ;

    a.value_changed += ( [] ( auto sender, auto v )
    { std::cout << "closure: value changed to " << v << "  sender: " << sender << '\n' ; } ) ;
    a.update(7) ;

    std::cout << "----------------\n" ;

    struct B
    {
       void on_value_changed( const A* sender, int v ) const
       { std::cout << "main::B:on_value_changed(" << v << ")  sender: " << sender << '\n' ; }
    };
    B b ;
    using namespace std::placeholders ;
    a.value_changed += std::bind( &B::on_value_changed, std::ref(b), _1, _2 ) ;
    a.update(22) ;

    std::cout << "----------------\n" ;
    a.value_changed += event_handler ;
    a.update(-12) ;
}

http://coliru.stacked-crooked.com/a/ad2681f412ef0fac
actually, SOMEONE out there has to think about this stuff. SOMEONE out there is coding your operating systems, and they have to ensure that the event stuff works.

Windows specifically has a messaging system where "events" are published to a global message store (its a queue, of sorts, or multiple queues, not sure exactly) and programs that care can check. You can listen to messages like "WM mouse-move" or "Shutdown is happening and stuff" or 100 or so other such things ... keypressed, and more.

Locally, your own program has its own handlers for events. These seem to be on a poll to listen basis. That is, somewhere in a thread, is a "did user press the exit button on the window frame?" in a loop with a short duration sleep or something along those lines (this is grossly oversimplified). If you press that button, it invokes either a pre-defined handler (exit the progam now!) or your own handler (prompt to save a file, clean up stuff, and exit after that). You can see the flawed polling system in action; if you hog the CPU and lock up your program in a tight loop so that it greys out and says "not responding", clicking the exit button does not work for a while... eventually it picks up the message, if the tight loop ends. Surely you have experienced this in a win program before :)




closed account (48T7M4Gy)
@OP

One place to start if you want to get into operating system programming (and associated event handling) is the source code for Linux.

Opinions will vary but even though Windows does the same I find the code has too much Windows stuff and tends to confuse the issues if you are just trying to come to grips with the basics.

Or maybe learn at a lower level ...

https://www.win.tue.nl/~aeb/linux/lk/lk-12.html
I prefer to use windows over Linux but yes, the Linux source is cleaner. A virtually infinite supply of good programmers working on stuff for free for 30+ years tends to shake out some decent code. I think trying to clean it up and make a stripped down windows would be an awesome project but M$ already provides an embedded OS version and the project to do it yourself would be enormous.

But I would try to find a smaller system than this even. Look to one of the micro embedded Linux versions; there are versions that are stripped way down to run on routers, wrist watches, and the like. There may even be a university that has done the work for you and you might find a learning version of the OS that has been simplified down for studying operating systems topics. Dunno what is out there, but try looking for these sorts of things.

One doesn't have to go the OS route to study events. One can continue on from JLBorges code, or maybe implement the class structure outlined in the link I gave.

Delving into the OS stuff is likely to be much harder, but not impossible.

Another thing to do is look at the header files in the boost library:

http://stackoverflow.com/questions/768351/complete-example-using-boostsignals-for-c-eventing#770662

http://svn.boost.org/svn/boost/trunk/libs/signals/example/

https://theboostcpplibraries.com/boost.signals2
closed account (48T7M4Gy)
But how do events actually work? What I mean is the actual underlying mechanism.
@kemort

Did you look at the link I posted, in particular the class diagram?
closed account (48T7M4Gy)
@TIM
I assume you mean the second post in this thread. Well done that person!

The reason I posted the quote from OP, interestingly just as my arch-nemesis borges did, is the various well established sources such as Linux cut to the chase at a lower level than subsequent abstractions which is what happens with patterns, boost and Microsoft Windows which I referred to in particular.

It's all grist for the mill and there is nothing wrong to my mind with advancing any of those abstracted ideas however the OS level crops up depending how far under the bonnet OP wants to go, hence even my vague reference to assembler. All of that decision-making is OP's call.

To close off options by contradicting other posters is just silly in my estimation. I'm sure you'd agree.
Topic archived. No new replies allowed.