properties-cpp  0.0.1
A very simple convenience library for handling properties and signals in C++11.
/build/properties-cpp-0.0.2+ubports+0~20210526142939.1+ubports16.04~1.gbp7dd9ec/README.md
Go to the documentation of this file.
1 properties-cpp {#mainpage}
2 ===========
3 
4 process-cpp is a simple header-only implementation of properties and
5 signals. It is meant to be used for developing low-level system
6 services. Its main features include:
7 
8  - Thread-safe signal invocation and observer mgmt.
9  - The ability to dispatch signal invocations via arbitrary event loops.
10  - Typed properties with an in-place update mechanism that avoids unneccessary deep copies.
11  - Well tested and documented.
12 
13 A Textfield With an Observable Cursor Position
14 ----------------------------------------------
15 
16 ~~~~~~~~~~~~~{.cpp}
17 namespace
18 {
19 struct TextField
20 {
21  void move_cursor_to(int new_position)
22  {
23  cursor_position.set(new_position);
24  }
25 
26  core::Property<int> cursor_position;
27 };
28 }
29 
30 TEST(Property, cursor_position_changes_are_transported_correctly)
31 {
32  int position = -1;
33 
34  TextField tf;
35 
36  // Setup a connection to the cursor position property
37  tf.cursor_position.changed().connect(
38  [&position](int value)
39  {
40  position = value;
41  });
42 
43  // Move the cursor
44  tf.move_cursor_to(22);
45 
46  // Check that the correct value has propagated
47  EXPECT_EQ(22, position);
48 }
49 ~~~~~~~~~~~~~
50 
51 Integrating With Arbitrary Event Loops/Reactor Implementations
52 --------------------------------------------------------------
53 ~~~~~~~~~~~~~{.cpp}
54 namespace
55 {
56 struct EventLoop
57 {
58  typedef std::function<void()> Handler;
59 
60  void stop()
61  {
62  stop_requested = true;
63  }
64 
65  void run()
66  {
67  while (!stop_requested)
68  {
69  std::unique_lock<std::mutex> ul(guard);
70  wait_condition.wait_for(
71  ul,
72  std::chrono::milliseconds{500},
73  [this]() { return handlers.size() > 0; });
74 
75  while (handlers.size() > 0)
76  {
77  handlers.front()();
78  handlers.pop();
79  }
80  }
81  }
82 
83  void dispatch(const Handler& h)
84  {
85  std::lock_guard<std::mutex> lg(guard);
86  handlers.push(h);
87  }
88 
89  bool stop_requested = false;
90  std::queue<Handler> handlers;
91  std::mutex guard;
92  std::condition_variable wait_condition;
93 };
94 }
95 
96 TEST(Signal, installing_a_custom_dispatcher_ensures_invocation_on_correct_thread)
97 {
98  // We instantiate an event loop and run it on a different thread than the main one.
99  EventLoop dispatcher;
100  std::thread dispatcher_thread{[&dispatcher]() { dispatcher.run(); }};
101  std::thread::id dispatcher_thread_id = dispatcher_thread.get_id();
102 
103  // The signal that we want to dispatch via the event loop.
104  core::Signal<int, double> s;
105 
106  static const int expected_invocation_count = 10000;
107 
108  // Setup the connection. For each invocation we check that the id of the
109  // thread the handler is being called upon equals the thread that the
110  // event loop is running upon.
111  auto connection = s.connect(
112  [&dispatcher, dispatcher_thread_id](int value, double d)
113  {
114  EXPECT_EQ(dispatcher_thread_id,
115  std::this_thread::get_id());
116 
117  std::cout << d << std::endl;
118 
119  if (value == expected_invocation_count)
120  dispatcher.stop();
121  });
122 
123  // Route the connection via the dispatcher
124  connection.dispatch_via(
125  std::bind(
126  &EventLoop::dispatch,
127  std::ref(dispatcher),
128  std::placeholders::_1));
129 
130  // Invoke the signal from the main thread.
131  for (unsigned int i = 1; i <= expected_invocation_count; i++)
132  s(i, 42.);
133 
134  if (dispatcher_thread.joinable())
135  dispatcher_thread.join();
136 }
137 ~~~~~~~~~~~~~