DSPatch v.10.2.3
Loading...
Searching...
No Matches
SignalBus.h
1/******************************************************************************
2DSPatch - The Refreshingly Simple C++ Dataflow Framework
3Copyright (c) 2024, Marcus Tomlinson
4
5BSD 2-Clause License
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are met:
9
101. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12
132. Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation
15 and/or other materials provided with the distribution.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27******************************************************************************/
28
29#pragma once
30
31#include <dspatch/Common.h>
32
33#include <fast_any/any.h>
34
35#include <vector>
36
37namespace DSPatch
38{
39
41
54class DLLEXPORT SignalBus final
55{
56public:
57 NONCOPYABLE( SignalBus );
58
59 inline SignalBus();
60 inline SignalBus( SignalBus&& );
61
62 inline void SetSignalCount( int signalCount );
63 inline int GetSignalCount() const;
64
65 inline fast_any::any* GetSignal( int signalIndex );
66
67 inline bool HasValue( int signalIndex ) const;
68
69 template <typename ValueType>
70 inline ValueType* GetValue( int signalIndex ) const;
71
72 template <typename ValueType>
73 inline void SetValue( int signalIndex, const ValueType& newValue );
74
75 template <typename ValueType>
76 inline void MoveValue( int signalIndex, ValueType&& newValue );
77
78 inline void SetSignal( int toSignalIndex, const fast_any::any& fromSignal );
79 inline void MoveSignal( int toSignalIndex, fast_any::any& fromSignal );
80
81 inline void ClearAllValues();
82
83 inline fast_any::type_info GetType( int signalIndex ) const;
84
85private:
86 std::vector<fast_any::any> _signals;
87};
88
89inline SignalBus::SignalBus() = default;
90
91// cppcheck-suppress missingMemberCopy
92inline SignalBus::SignalBus( SignalBus&& rhs )
93 : _signals( std::move( rhs._signals ) )
94{
95}
96
97inline void SignalBus::SetSignalCount( int signalCount )
98{
99 _signals.resize( signalCount );
100}
101
102inline int SignalBus::GetSignalCount() const
103{
104 return (int)_signals.size();
105}
106
107inline fast_any::any* SignalBus::GetSignal( int signalIndex )
108{
109 // You might be thinking: Why the raw pointer return here?
110
111 // This is for usability, design, and performance reasons. Usability, because a pointer allows
112 // the user to manipulate the contained value externally. Design, because DSPatch doesn't use
113 // exceptions - a nullptr return here is the equivalent of "signal does not exist".
114 // Performance, because returning a smart pointer means having to store the value as a smart
115 // pointer too - this adds yet another level of indirection to the value, as well as some
116 // reference counting overhead. These Get() and Set() methods are VERY frequently called, so
117 // doing as little as possible with the data here is best.
118
119 if ( (size_t)signalIndex < _signals.size() )
120 {
121 return &_signals[signalIndex];
122 }
123 else
124 {
125 return nullptr;
126 }
127}
128
129inline bool SignalBus::HasValue( int signalIndex ) const
130{
131 if ( (size_t)signalIndex < _signals.size() )
132 {
133 return _signals[signalIndex].has_value();
134 }
135 else
136 {
137 return false;
138 }
139}
140
141template <typename ValueType>
142inline ValueType* SignalBus::GetValue( int signalIndex ) const
143{
144 // You might be thinking: Why the raw pointer return here?
145
146 // See: GetSignal().
147
148 if ( (size_t)signalIndex < _signals.size() )
149 {
150 return _signals[signalIndex].as<ValueType>();
151 }
152 else
153 {
154 return nullptr;
155 }
156}
157
158template <typename ValueType>
159inline void SignalBus::SetValue( int signalIndex, const ValueType& newValue )
160{
161 if ( (size_t)signalIndex < _signals.size() )
162 {
163 _signals[signalIndex].emplace<ValueType>( newValue );
164 }
165}
166
167template <typename ValueType>
168inline void SignalBus::MoveValue( int signalIndex, ValueType&& newValue )
169{
170 if ( (size_t)signalIndex < _signals.size() )
171 {
172 _signals[signalIndex].emplace<ValueType>( std::forward<ValueType>( newValue ) );
173 }
174}
175
176inline void SignalBus::SetSignal( int toSignalIndex, const fast_any::any& fromSignal )
177{
178 if ( (size_t)toSignalIndex < _signals.size() )
179 {
180 _signals[toSignalIndex] = fromSignal;
181 }
182}
183
184inline void SignalBus::MoveSignal( int toSignalIndex, fast_any::any& fromSignal )
185{
186 // You might be thinking: Why swap and not move here?
187
188 // This is a really nifty little optimisation actually. When we move a signal value from an
189 // output to an input (or vice-versa within a component) we move its type_info along with it.
190 // If you look at any::emplace(), you'll see that type_info is really useful in determining
191 // whether we need to delete and copy (re)construct our contained value, or can simply copy
192 // assign. To avoid the former as much as possible, a swap is done between source and target
193 // signals such that, between these two points, just two value holders need to be constructed,
194 // and shared back and forth from then on.
195
196 if ( (size_t)toSignalIndex < _signals.size() )
197 {
198 _signals[toSignalIndex].swap( fromSignal );
199 }
200}
201
202inline void SignalBus::ClearAllValues()
203{
204 for ( auto& signal : _signals )
205 {
206 signal.reset();
207 }
208}
209
210inline fast_any::type_info SignalBus::GetType( int signalIndex ) const
211{
212 if ( (size_t)signalIndex < _signals.size() )
213 {
214 return _signals[signalIndex].type();
215 }
216 else
217 {
218 return fast_any::type_id<void>;
219 }
220}
221
222} // namespace DSPatch
Signal container.
Definition SignalBus.h:55