DSPatch v.9.7.5
Loading...
Searching...
No Matches
SignalBus.h
1/******************************************************************************
2DSPatch - The Refreshingly Simple C++ Dataflow Framework
3Copyright (c) 2023, 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
56class DLLEXPORT SignalBus final
57{
58public:
59 NONCOPYABLE( SignalBus );
60
61 inline SignalBus();
62 inline SignalBus( SignalBus&& );
63
64 inline void SetSignalCount( int signalCount );
65 inline int GetSignalCount() const;
66
67 inline fast_any::any* GetSignal( int signalIndex );
68
69 inline bool HasValue( int signalIndex ) const;
70
71 template <typename ValueType>
72 inline ValueType* GetValue( int signalIndex ) const;
73
74 template <typename ValueType>
75 inline void SetValue( int signalIndex, const ValueType& newValue );
76
77 template <typename ValueType>
78 inline void MoveValue( int signalIndex, ValueType&& newValue );
79
80 inline void SetSignal( int toSignalIndex, const fast_any::any& fromSignal );
81 inline void MoveSignal( int toSignalIndex, fast_any::any& fromSignal );
82
83 inline void ClearAllValues();
84
85 inline const fast_any::type_info& GetType( int signalIndex ) const;
86
87private:
88 std::vector<fast_any::any> _signals;
89};
90
91inline SignalBus::SignalBus() = default;
92
93// cppcheck-suppress missingMemberCopy
94inline SignalBus::SignalBus( SignalBus&& rhs )
95 : _signals( std::move( rhs._signals ) )
96{
97}
98
99inline void SignalBus::SetSignalCount( int signalCount )
100{
101 _signals.resize( signalCount );
102}
103
104inline int SignalBus::GetSignalCount() const
105{
106 return (int)_signals.size();
107}
108
109inline fast_any::any* SignalBus::GetSignal( int signalIndex )
110{
111 // You might be thinking: Why the raw pointer return here?
112
113 // This is for usability, design, and performance reasons. Usability, because a pointer allows
114 // the user to manipulate the contained value externally. Design, because DSPatch doesn't use
115 // exceptions - a nullptr return here is the equivalent of "signal does not exist".
116 // Performance, because returning a smart pointer means having to store the value as a smart
117 // pointer too - this adds yet another level of indirection to the value, as well as some
118 // reference counting overhead. These Get() and Set() methods are VERY frequently called, so
119 // doing as little as possible with the data here is best.
120
121 if ( (size_t)signalIndex < _signals.size() )
122 {
123 return &_signals[signalIndex];
124 }
125 else
126 {
127 return nullptr;
128 }
129}
130
131inline bool SignalBus::HasValue( int signalIndex ) const
132{
133 if ( (size_t)signalIndex < _signals.size() )
134 {
135 return _signals[signalIndex].has_value();
136 }
137 else
138 {
139 return false;
140 }
141}
142
143template <typename ValueType>
144inline ValueType* SignalBus::GetValue( int signalIndex ) const
145{
146 // You might be thinking: Why the raw pointer return here?
147
148 // See: GetSignal().
149
150 if ( (size_t)signalIndex < _signals.size() )
151 {
152 return _signals[signalIndex].as<ValueType>();
153 }
154 else
155 {
156 return nullptr;
157 }
158}
159
160template <typename ValueType>
161inline void SignalBus::SetValue( int signalIndex, const ValueType& newValue )
162{
163 if ( (size_t)signalIndex < _signals.size() )
164 {
165 _signals[signalIndex].emplace<ValueType>( newValue );
166 }
167}
168
169template <typename ValueType>
170inline void SignalBus::MoveValue( int signalIndex, ValueType&& newValue )
171{
172 if ( (size_t)signalIndex < _signals.size() )
173 {
174 _signals[signalIndex].emplace<ValueType>( std::move( newValue ) );
175 }
176}
177
178inline void SignalBus::SetSignal( int toSignalIndex, const fast_any::any& fromSignal )
179{
180 if ( (size_t)toSignalIndex < _signals.size() )
181 {
182 _signals[toSignalIndex] = fromSignal;
183 }
184}
185
186inline void SignalBus::MoveSignal( int toSignalIndex, fast_any::any& fromSignal )
187{
188 // You might be thinking: Why swap and not move here?
189
190 // This is a really nifty little optimisation actually. When we move a signal value from an
191 // output to an input (or vice-versa within a component) we move its type_info along with it.
192 // If you look at any::emplace(), you'll see that type_info is really useful in determining
193 // whether we need to delete and copy (re)construct our contained value, or can simply copy
194 // assign. To avoid the former as much as possible, a swap is done between source and target
195 // signals such that, between these two points, just two value holders need to be constructed,
196 // and shared back and forth from then on.
197
198 if ( (size_t)toSignalIndex < _signals.size() )
199 {
200 _signals[toSignalIndex].swap( fromSignal );
201 }
202}
203
204inline void SignalBus::ClearAllValues()
205{
206 for ( auto& signal : _signals )
207 {
208 signal.reset();
209 }
210}
211
212inline const fast_any::type_info& SignalBus::GetType( int signalIndex ) const
213{
214 if ( (size_t)signalIndex < _signals.size() )
215 {
216 return _signals[signalIndex].type();
217 }
218 else
219 {
220 return fast_any::type_id<void>;
221 }
222}
223
224} // namespace DSPatch
Signal container.
Definition SignalBus.h:57