DSPatch v.11.2.0
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 "../fast_any/any.h"
32
33#include <vector>
34
35namespace DSPatch
36{
37
39
52class SignalBus final
53{
54public:
55 SignalBus( const SignalBus& ) = delete;
56 SignalBus& operator=( const SignalBus& ) = delete;
57
58 SignalBus();
60 ~SignalBus();
61
62 void SetSignalCount( int signalCount );
63 int GetSignalCount() const;
64
65 fast_any::any* GetSignal( int signalIndex );
66
67 bool HasValue( int signalIndex ) const;
68
69 template <typename ValueType>
70 ValueType* GetValue( int signalIndex ) const;
71
72 template <typename ValueType>
73 void SetValue( int signalIndex, const ValueType& newValue );
74
75 template <typename ValueType>
76 void MoveValue( int signalIndex, ValueType&& newValue );
77
78 void SetSignal( int toSignalIndex, const fast_any::any& fromSignal );
79 void MoveSignal( int toSignalIndex, fast_any::any& fromSignal );
80
81 void ClearAllValues();
82
83 fast_any::type_info GetType( int signalIndex ) const;
84
85private:
86 std::vector<fast_any::any> _signals;
87};
88
89inline SignalBus::SignalBus() = default;
90
91inline SignalBus::SignalBus( SignalBus&& rhs )
92 : _signals( std::move( rhs._signals ) )
93{
94}
95
96inline SignalBus::~SignalBus() = default;
97
98inline void SignalBus::SetSignalCount( int signalCount )
99{
100 _signals.resize( signalCount );
101}
102
103inline int SignalBus::GetSignalCount() const
104{
105 return (int)_signals.size();
106}
107
108inline fast_any::any* SignalBus::GetSignal( int signalIndex )
109{
110 // You might be thinking: Why the raw pointer return here?
111
112 // This is for usability and performance reasons. Usability, because a pointer allows the user
113 // to manipulate the contained value externally. Performance, because returning a smart pointer
114 // means having to store the value as a smart pointer too - this adds yet another level of
115 // indirection to the value, as well as some reference counting overhead. These Get() and Set()
116 // methods are VERY frequently called, so doing as little as possible with the data here is best.
117
118 return &_signals[signalIndex];
119}
120
121inline bool SignalBus::HasValue( int signalIndex ) const
122{
123 return _signals[signalIndex].has_value();
124}
125
126template <typename ValueType>
127inline ValueType* SignalBus::GetValue( int signalIndex ) const
128{
129 // You might be thinking: Why the raw pointer return here?
130
131 // See: GetSignal().
132
133 return _signals[signalIndex].as<ValueType>();
134}
135
136template <typename ValueType>
137inline void SignalBus::SetValue( int signalIndex, const ValueType& newValue )
138{
139 _signals[signalIndex].emplace<ValueType>( newValue );
140}
141
142template <typename ValueType>
143inline void SignalBus::MoveValue( int signalIndex, ValueType&& newValue )
144{
145 _signals[signalIndex].emplace<ValueType>( std::forward<ValueType>( newValue ) );
146}
147
148inline void SignalBus::SetSignal( int toSignalIndex, const fast_any::any& fromSignal )
149{
150 _signals[toSignalIndex].emplace( fromSignal );
151}
152
153inline void SignalBus::MoveSignal( int toSignalIndex, fast_any::any& fromSignal )
154{
155 // You might be thinking: Why swap and not move here?
156
157 // This is a really nifty little optimisation actually. When we move a signal value from an
158 // output to an input (or vice-versa within a component) we move its type_info along with it.
159 // If you look at any::emplace(), you'll see that type_info is really useful in determining
160 // whether we need to delete and copy (re)construct our contained value, or can simply copy
161 // assign. To avoid the former as much as possible, a swap is done between source and target
162 // signals such that, between these two points, just two value holders need to be constructed,
163 // and shared back and forth from then on.
164
165 _signals[toSignalIndex].swap( fromSignal );
166}
167
168inline void SignalBus::ClearAllValues()
169{
170 for ( auto& signal : _signals )
171 {
172 signal.reset();
173 }
174}
175
176inline fast_any::type_info SignalBus::GetType( int signalIndex ) const
177{
178 return _signals[signalIndex].type();
179}
180
181} // namespace DSPatch
Signal container.
Definition SignalBus.h:53