]> git.lyx.org Git - lyx.git/blob - 3rdparty/nod/README.md
Update credits
[lyx.git] / 3rdparty / nod / README.md
1 # Nod
2 [![Build Status](https://travis-ci.org/fr00b0/nod.svg?branch=master)](https://travis-ci.org/fr00b0/nod)
3 [![GitHub tag](https://img.shields.io/github/tag/fr00b0/nod.svg?label=version)](https://github.com/fr00b0/nod/releases)
4
5 Dependency free, header only signals and slot library implemented with C++11.
6
7 ## Usage
8
9 ### Simple usage
10 The following example creates a signal and then connects a lambda as a slot.
11
12 ```cpp
13 // Create a signal which accepts slots with no arguments and void return value.
14 nod::signal<void()> signal;
15 // Connect a lambda slot that writes "Hello, World!" to stdout
16 signal.connect([](){
17                 std::cout << "Hello, World!" << std::endl;
18         });
19 // Call the slots
20 signal();
21 ```
22
23 ### Connecting multiple slots
24 If multiple slots are connected to the same signal, all of the slots will be
25 called when the signal is invoked. The slots will be called in the same order
26 as they where connected.
27
28 ```cpp
29 void endline() {
30         std::cout << std::endl;
31 }
32
33 // Create a signal
34 nod::signal<void()> signal;
35 // Connect a lambda that prints a message
36 signal.connect([](){
37                 std::cout << "Message without endline!";
38         });
39 // Connect a function that prints a endline
40 signal.connect(endline);
41
42 // Call the slots
43 signal();
44 ```
45
46 #### Slot type
47 The signal types in the library support connection of the same types that is
48 supported by `std::function<T>`.
49
50 ### Slot arguments
51 When a signal calls it's connected slots, any arguments passed to the signal
52 are propagated to the slots. To make this work, we do need to specify the 
53 signature of the signal to accept the arguments.
54
55 ```cpp
56 void print_sum( int x, int y ) {
57         std::cout << x << "+" << y << "=" << (x+y) << std::endl;
58 }
59 void print_product( int x, int y ) {
60         std::cout << x << "*" << y << "=" << (x*y) << std::endl;
61 }
62
63
64 // We create a signal with two integer arguments.
65 nod::signal<void(int,int)> signal;
66 // Let's connect our slot
67 signal.connect( print_sum );
68 signal.connect( print_product );
69
70 // Call the slots
71 signal(10, 15);
72 signal(-5, 7);  
73
74 ```
75
76 ### Disconnecting slots
77 There are many circumstances where the programmer needs to diconnect a slot that
78 no longer want to recieve events from the signal. This can be really important
79 if the lifetime of the slots are shorter than the lifetime of the signal. That
80 could cause the signal to call slots that have been destroyed but not
81 disconnected, leading to undefined behaviour and probably segmentation faults.
82
83 When a slot is connected, the return value from the  `connect` method returns
84 an instance of the class `nod::connection`, that can be used to disconnect
85 that slot.
86
87 ```cpp
88 // Let's create a signal
89 nod::signal<void()> signal;
90 // Connect a slot, and save the connection
91 nod::connection connection = signal.connect([](){
92                                                                  std::cout << "I'm connected!" << std::endl;
93                                                          });
94 // Triggering the signal will call the slot
95 signal();
96 // Now we disconnect the slot
97 connection.disconnect();
98 // Triggering the signal will no longer call the slot
99 signal();
100 ```     
101
102 ### Scoped connections
103 To assist in disconnecting slots, one can use the class `nod::scoped_connection`
104 to capture a slot connection. A scoped connection will automatically disconnect
105 the slot when the connection object goes out of scope.
106
107 ```cpp
108 // We create a signal
109 nod::signal<void()> signal;
110 // Let's use a scope to control lifetime
111
112         // Let's save the connection in a scoped_connection
113         nod::scoped_connection connection =
114                 signal.connect([](){
115                         std::cout << "This message should only be emitted once!" << std::endl; 
116                 });
117         // If we trigger the signal, the slot will be called
118         signal();
119 } // Our scoped connection is destructed, and disconnects the slot
120 // Triggering the signal now will not call the slot
121 signal();       
122 ```
123
124 ### Slot return values
125
126 #### Accumulation of return values
127 It is possible for slots to have a return value. The return values can be
128 returned from the signal using a *accumulator*, which is a function object that
129 acts as a proxy object that processes the slot return values. When triggering a
130 signal through a accumulator, the accumulator gets called for each slot return
131 value, does the desired accumulation and then return the result to the code
132 triggering the signal. The accumulator is designed to work in a similar way as 
133 the STL numerical algorithm `std::accumulate`.
134
135 ```cpp
136 // We create a singal with slots that return a value
137 nod::signal<int(int, int)> signal;
138 // Then we connect some signals
139 signal.connect( std::plus<int>{} );
140 signal.connect( std::multiplies<int>{} );
141 signal.connect( std::minus<int>{} );            
142 // Let's say we want to calculate the sum of all the slot return values
143 // when triggering the singal with the parameters 10 and 100.
144 // We do this by accumulating the return values with the initial value 0
145 // and a plus function object, like so:
146 std::cout << "Sum: " << signal.accumulate(0, std::plus<int>{})(10,100) << std::endl;
147 // Or accumulate by multiplying (this needs 1 as initial value):
148 std::cout << "Product: " << signal.accumulate(1, std::multiplies<int>{})(10,100) << std::endl;
149 // If we instead want to build a vector with all the return values
150 // we can accumulate them this way (start with a empty vector and add each value):                      
151 auto vec = signal.accumulate( std::vector<int>{}, []( std::vector<int> result, int value ) {
152                 result.push_back( value );
153                 return result;
154         })(10,100);
155
156 std::cout << "Vector: ";
157 for( auto const& element : vec ) {
158         std::cout << element << " "; 
159 }
160 std::cout << std::endl;
161 ```
162 #### Aggregation
163 As we can see from the previous example, we can use the `accumulate` method if
164 we want to aggregate all the return values of the slots. Doing the aggregation
165 that way is not very optimal. It is both a inefficient algorithm for doing
166 aggreagtion to a container, and it obscures the call site as the caller needs to
167 express the aggregation using the verb *accumulate*. To remedy these
168 shortcomings we can turn to the method `aggregate` instead. This is a template
169 method, taking the type of container to aggregate to as a template parameter.
170
171 ```cpp
172 // We create a singal
173 nod::signal<int(int, int)> signal;
174 // Let's connect some slots
175 signal.connect( std::plus<int>{} );
176 signal.connect( std::multiplies<int>{} );
177 signal.connect( std::minus<int>{} );
178 // We can now trigger the signal and aggregate the slot return values
179 auto vec = signal.aggregate<std::vector<int>>(10,100);
180
181 std::cout << "Result: ";
182 for( auto const& element : vec ) {
183         std::cout << element << " "; 
184 }
185 std::cout << std::endl;
186 ```
187
188 ## Thread safety
189 There are two types of signals in the library. The first is `nod::signal<T>`
190 which is safe to use in a multi threaded environment. Multiple threads can read,
191 write, connect slots and disconnect slots simultaneously, and the signal will 
192 provide the nessesary synchronization. When triggering a slignal, all the
193 registered slots will be called and executed by the thread that triggered the
194 signal.
195
196 The second type of signal is `nod::unsafe_signal<T>` which is **not** safe to
197 use in a multi threaded environment. No syncronization will be performed on the
198 internal state of the signal. Instances of the signal should theoretically be
199 safe to read from multiple thread simultaneously, as long as no thread is
200 writing to the same object at the same time. There can be a performance gain
201 involved in using the unsafe version of a signal, since no syncronization
202 primitives will be used.
203
204 `nod::connection` and `nod::scoped_connection` are thread safe for reading from
205 multiple threads, as long as no thread is writing to the same object. Writing in
206 this context means calling any non const member function, including destructing
207 the object. If an object is being written by one thread, then all reads and
208 writes to that object from the same or other threads needs to be prevented.
209 This basically means that a connection is only allowed to be disconnected from
210 one thread, and you should not check connection status or reassign the
211 connection while it is being disconnected.
212
213 ## Building the tests
214 The test project uses [premake5](https://premake.github.io/download.html) to 
215 generate make files or similiar.
216
217 ### Linux
218 To build and run the tests using gcc and gmake on linux, execute the following
219 from the test directory:
220 ```bash
221 premake5 gmake
222 make -C build/gmake
223 bin/gmake/debug/nod_tests
224 ```
225
226 ### Visual Studio 2013
227 To build and run the tests, execute the following from the test directory:
228
229 ```batchfile
230 REM Adjust paths to suite your environment
231 c:\path\to\premake\premake5.exe vs2013
232 "c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\vsvars32.bat"
233 msbuild /m build\vs2013\nod_tests.sln
234 bin\vs2013\debug\nod_tests.exe
235 ```
236
237 ## The MIT License (MIT)
238
239 Copyright (c) 2015 Fredrik Berggren
240
241 Permission is hereby granted, free of charge, to any person obtaining a copy
242 of this software and associated documentation files (the "Software"), to deal
243 in the Software without restriction, including without limitation the rights
244 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
245 copies of the Software, and to permit persons to whom the Software is
246 furnished to do so, subject to the following conditions:
247
248 The above copyright notice and this permission notice shall be included in all
249 copies or substantial portions of the Software.
250
251 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
252 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
253 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
254 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
255 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
256 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
257 SOFTWARE.