ImFusion C++ SDK 4.4.0
ImFusion::StreamGroup< Proxy > Class Template Reference

#include <ImFusion/Stream/StreamGroup.h>

Templated stream group class for bundling multiple streams using the CRTP pattern. More...

Inheritance diagram for ImFusion::StreamGroup< Proxy >:

Detailed Description

template<class Proxy>
class ImFusion::StreamGroup< Proxy >

Templated stream group class for bundling multiple streams using the CRTP pattern.

StreamGroup enables a proxy stream to manage an arbitrary number of substreams, coordinating their lifecycle and data emission. The proxy stream (template parameter Proxy) can be any class deriving from Stream (e.g., ImageStream, TrackingStream) or Stream itself, and serves as the representative and controller for all substreams.

Key features:

  • Substreams can be added or removed dynamically at any time after construction
  • Thread-safe substream management (adding and removing) with automatic cleanup
  • Coordinated state synchronization across all substreams
  • Dual emission of CompoundStreamData (all streams) and individual StreamData (per stream)

The proxy stream appears as a normal Stream in the data model, while substreams appear as child data objects through the CompoundData interface.

Example implementation for a tracking device with two camera streams:

// Stream group example using dummy image substreams
class MyStreamGroup : public TrackingStream, public StreamGroup<MyStreamGroup>
{
public:
MyStreamGroup()
: TrackingStream("My Tracking Stream")
{
// Add image substreams for camera data
addSubstream(std::make_unique<ImageSubstream>(this, "Left Camera"));
addSubstream(std::make_unique<ImageSubstream>(this, "Right Camera"));
}
protected:
std::optional<WorkContinuation> doWork() override
{
// Compute next interation time based on desired framerate
double fps = 30.0;
auto nextIterationTime = std::chrono::system_clock::now() + std::chrono::microseconds(static_cast<int>(1000000.0 / fps));
// Acquire data from all sources
std::vector<TrackingInstrument> trackingData = acquireTrackingData();
SharedImageSet leftImageData = acquireLeftImage();
auto rightImageData = acquireRightImage();
// Create stream data for all streams
auto proxyData = std::make_shared<TrackingStreamData>(this, trackingData);
auto leftData = std::make_shared<ImageStreamData>(substreams()[0], std::move(leftImageData));
auto rightData = std::make_shared<ImageStreamData>(substreams()[1], std::move(rightImageData));
// Emit all data together
auto substreamStreamData = std::vector<std::shared_ptr<const StreamData>>{leftData, rightData};
auto compound = std::make_shared<CompoundStreamData>(proxyData, std::move(substreamStreamData));
emitMainAndSubSignals(compound);
return WorkContinuation{nextIterationTime};
}
};
Template Parameters
ProxyThe proxy stream class, which must derive from Stream and typically uses CRTP (e.g., class MyGroup : public Stream, public StreamGroup<MyGroup>).
See also
Substream, SubstreamWrapper, CompoundStreamData, StreamGroupBase

Public Member Functions

 StreamGroup (StreamGroup &&) noexcept
StreamGroup & operator= (StreamGroup &&other) noexcept
StreamproxyStream () override
 Returns the non-owning pointer to the proxy stream, valid for the lifetime of the stream group.
DataList children () const override
 Returns substreams as a non-owning list for the CompoundData interface.
std::vector< Stream * > substreams () const override
 Returns all substreams in this stream group.
Public Member Functions inherited from ImFusion::SignalReceiver
 SignalReceiver ()=default
 Default constructor.
 SignalReceiver (const SignalReceiver &other)
 Copy constructor, does not copy any existing signal connections from other.
SignalReceiveroperator= (SignalReceiver rhs)
 Assignment operator, disconnects all existing connections, does not copy any existing signal connections from rhs.
virtual ~SignalReceiver ()
 Virtual destructor disconnects from all connected signals.

Protected Member Functions

bool addSubstream (std::unique_ptr< SubstreamBase > substream) override
 Adds a substream to the stream group.
bool removeSubstream (Stream *substream) override
 Removes a substream from the stream group.
void emitMainAndSubSignals (std::shared_ptr< const CompoundStreamData > tuple, bool emitSubstreamSignals=true)
 Emits stream data via both compound and individual signals.
Protected Member Functions inherited from ImFusion::StreamGroupBase
void reassignSubstream (SubstreamBase *substream)
 Reassigns a substream to this stream group.
Protected Member Functions inherited from ImFusion::Utils::NotCopyable
 NotCopyable (NotCopyable &&) noexcept=default
NotCopyable & operator= (NotCopyable &&) noexcept=default
 NotCopyable (const NotCopyable &)=delete
NotCopyable & operator= (const NotCopyable &)=delete
Protected Member Functions inherited from ImFusion::SignalReceiver
void disconnectAll ()
 Disconnects all existing connections.

Additional Inherited Members

Public Attributes inherited from ImFusion::StreamGroupBase
ProtectedSignal< std::shared_ptr< const CompoundStreamData > > signalNewStreamGroupData
 Signal emitted when new stream data is available from the stream group.
Public Attributes inherited from ImFusion::CompoundData
Signal< Data * > signalChildAdded
 Signal emitted when a new Data instance has been added to children().
Signal< Data * > signalChildMoved
 Signal emitted when a Data instance has changed its position in children().
Signal< Data * > signalChildAboutToBeRemoved
 Signal emitted when a Data instance is about to be removed or taken from children().

Member Function Documentation

◆ proxyStream()

template<class Proxy>
Stream * ImFusion::StreamGroup< Proxy >::proxyStream ( )
overridevirtual

Returns the non-owning pointer to the proxy stream, valid for the lifetime of the stream group.

Convenience method that casts the stream group to its proxy stream.

Implements ImFusion::StreamGroupBase.

◆ children()

template<class Proxy>
DataList ImFusion::StreamGroup< Proxy >::children ( ) const
overridevirtualthreadsafe

Returns substreams as a non-owning list for the CompoundData interface.

This method provides the child data objects for the CompoundData interface, enabling the stream group to appear as a hierarchical data structure in the data model. See substreams() for details on thread safety and livetime guarantees.

See also
CompoundData, substreams()

Can be called concurrently from any thread.

Implements ImFusion::CompoundData.

◆ substreams()

template<class Proxy>
std::vector< Stream * > ImFusion::StreamGroup< Proxy >::substreams ( ) const
overridevirtualthreadsafe

Returns all substreams in this stream group.

This method is thread-safe and returns a snapshot of the current substreams. The returned pointers remain valid even if substreams are removed concurrently, provided the removal happens during a doWork() execution and emitMainAndSubSignals() is called before the method returns.

Thread-safety guarantee: Substreams are guaranteed not to be deleted within a doWork() execution until emitMainAndSubSignals() completes. This allows safe iteration over substreams even if they are removed from another thread during the iteration.

Safe usage pattern in doWork():

{
auto streams = substreams(); // Get snapshot of current substreams
// Safe to iterate even if another thread removes a substream
// The snapshot may become outdated (streams != substreams()) but remains valid
for (auto& s : streams)
{
// Prepare stream data from each stream
}
auto compound = std::make_shared<CompoundStreamData>(proxyData, substreamData);
emitMainAndSubSignals(compound); // Actual deletion of removed substreams happens here
return WorkContinuation{nextIterationTime};
}
std::vector< Stream * > substreams() const override
Returns all substreams in this stream group.
Definition StreamGroup.h:307
void emitMainAndSubSignals(std::shared_ptr< const CompoundStreamData > tuple, bool emitSubstreamSignals=true)
Emits stream data via both compound and individual signals.
Definition StreamGroup.h:378
T make_shared(T... args)

Can be called concurrently from any thread.

Implements ImFusion::StreamGroupBase.

◆ addSubstream()

template<class Proxy>
bool ImFusion::StreamGroup< Proxy >::addSubstream ( std::unique_ptr< SubstreamBase > substream)
overrideprotectedvirtualthreadsafe

Adds a substream to the stream group.

The substream must have been constructed with this stream group as its parent. Ownership is transferred to the stream group. The substream will be added to the data model as a child of this stream group.

Returns true if the substream was added successfully, false if the substream's parent does not match this group.

Can be called concurrently from any thread.

Implements ImFusion::StreamGroupBase.

◆ removeSubstream()

template<class Proxy>
bool ImFusion::StreamGroup< Proxy >::removeSubstream ( Stream * substream)
overrideprotectedvirtualthreadsafe

Removes a substream from the stream group.

The removal is always executed on the main thread to ensure data model consistency. If called from a worker thread, the actual removal is invoked on the main thread. The substream is not immediately deleted but moved to a garbage collection vector, with actual deletion occurring at the end of the next emitMainAndSubSignals() call.

Returns true if the substream was removed successfully, false if not found.

Can be called concurrently from any thread.

Implements ImFusion::StreamGroupBase.

◆ emitMainAndSubSignals()

template<class Proxy>
void ImFusion::StreamGroup< Proxy >::emitMainAndSubSignals ( std::shared_ptr< const CompoundStreamData > tuple,
bool emitSubstreamSignals = true )
protected

Emits stream data via both compound and individual signals.

This method handles the coordinated emission of StreamData from all streams in the group:

  1. Emits the proxy stream's StreamData via its Stream::signalStreamData (if not nullptr)
  2. Emits the CompoundStreamData via StreamGroupBase::signalNewStreamGroupData
  3. Emits each substream's StreamData via its Stream::signalStreamData (if emitSubstreamSignals is true)

This dual emission strategy allows receivers to either:

  • Process all streams together by connecting to signalNewStreamGroupData
  • Process individual streams by connecting to each stream's signalStreamData

Thread safety: This method must be called after all operations working on a local copy of substreams() in the doWork() method. Actual deletion of removed substreams occurs at the end of this call, ensuring safe iteration in doWork().

Use this method instead of directly emitting signals to ensure consistency and thread safety.

Parameters
tupleShared pointer to the CompoundStreamData containing all stream data to emit.
emitSubstreamSignalsIf false, skips emitting individual substream signals. Useful when substreams emit their own StreamData independently to avoid double emission.

The documentation for this class was generated from the following file:
  • ImFusion/Stream/StreamGroup.h
Search Tab / S to search, Esc to close