File: README.md

package info (click to toggle)
properties-cpp 0.0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 336 kB
  • sloc: cpp: 673; makefile: 21
file content (137 lines) | stat: -rw-r--r-- 3,760 bytes parent folder | download | duplicates (5)
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
properties-cpp         {#mainpage}
===========

process-cpp is a simple header-only implementation of properties and
signals. It is meant to be used for developing low-level system
services. Its main features include:

 - Thread-safe signal invocation and observer mgmt.
 - The ability to dispatch signal invocations via arbitrary event loops.
 - Typed properties with an in-place update mechanism that avoids unneccessary deep copies.
 - Well tested and documented.

A Textfield With an Observable Cursor Position
----------------------------------------------

~~~~~~~~~~~~~{.cpp}
namespace
{
struct TextField
{
    void move_cursor_to(int new_position)
    {
        cursor_position.set(new_position);
    }
    
    core::Property<int> cursor_position;
};
}

TEST(Property, cursor_position_changes_are_transported_correctly)
{
    int position = -1;

    TextField tf;
    
    // Setup a connection to the cursor position property
    tf.cursor_position.changed().connect(
        [&position](int value) 
        { 
            position = value; 
        });

    // Move the cursor
    tf.move_cursor_to(22);

    // Check that the correct value has propagated
    EXPECT_EQ(22, position);
}
~~~~~~~~~~~~~

Integrating With Arbitrary Event Loops/Reactor Implementations
--------------------------------------------------------------
~~~~~~~~~~~~~{.cpp}
namespace
{
struct EventLoop
{
    typedef std::function<void()> Handler;

    void stop()
    {
        stop_requested = true;
    }

    void run()
    {
        while (!stop_requested)
        {
            std::unique_lock<std::mutex> ul(guard);
            wait_condition.wait_for(
                        ul,
                        std::chrono::milliseconds{500},
                        [this]() { return handlers.size() > 0; });

            while (handlers.size() > 0)
            {
                handlers.front()();
                handlers.pop();
            }
        }
    }

    void dispatch(const Handler& h)
    {
        std::lock_guard<std::mutex> lg(guard);
        handlers.push(h);
    }

    bool stop_requested = false;
    std::queue<Handler> handlers;
    std::mutex guard;
    std::condition_variable wait_condition;
};
}

TEST(Signal, installing_a_custom_dispatcher_ensures_invocation_on_correct_thread)
{
    // We instantiate an event loop and run it on a different thread than the main one.
    EventLoop dispatcher;
    std::thread dispatcher_thread{[&dispatcher]() { dispatcher.run(); }};
    std::thread::id dispatcher_thread_id = dispatcher_thread.get_id();

    // The signal that we want to dispatch via the event loop.
    core::Signal<int, double> s;

    static const int expected_invocation_count = 10000;

    // Setup the connection. For each invocation we check that the id of the
    // thread the handler is being called upon equals the thread that the
    // event loop is running upon.
    auto connection = s.connect(
                [&dispatcher, dispatcher_thread_id](int value, double d)
                {
                    EXPECT_EQ(dispatcher_thread_id,
                              std::this_thread::get_id());

                    std::cout << d << std::endl;

                    if (value == expected_invocation_count)
                        dispatcher.stop();
                });

    // Route the connection via the dispatcher
    connection.dispatch_via(
                std::bind(
                    &EventLoop::dispatch,
                    std::ref(dispatcher),
                    std::placeholders::_1));

    // Invoke the signal from the main thread.
    for (unsigned int i = 1; i <= expected_invocation_count; i++)
        s(i, 42.);

    if (dispatcher_thread.joinable())
        dispatcher_thread.join();
}
~~~~~~~~~~~~~