I wish to design a type-safe NxM cross-connect. This is a mechanism that allows any of N function outputs to be connected to any of M inputs. Or to put it another way, to connect the return value a function (from a set of functions) with a parameter of another. A key aspect is that these connections must be configurable at runtime. The main issue I have is that the type of the inputs will differ from the outputs depending on each connection.
Let's say at some arbitrary point in the program I want to connect the return value of float foo()
to the parameter val
in void bar(double val)
.
I currently have two parameterised classes representing the inputs and outputs, named Source<T>
and Dest<U>
. T & U are expected to be float, double, bool, int, unsigned int, etc. I have a heterogeneous collection of Source<T>
instances, some double, some float, some int, some bool. There is also a heterogeneous collection of Dest<U>
instances, also some double, some float, some int, some bool. Sometimes T and U are the same type. For more context, these instances are all associated with unique names, allowing the higher level system to say "now please connect source::foo with dest::bar", or "remove the association between source::red and dest::blue".
In the previous example, at compile time, the type of the return value from foo()
is known by the programmer to be float and can be specified in source, so a Source<float>
can be instantiated and associated with this function. And the type of the parameter to bar()
is also known to be double so a Dest<double>
can be instantiated and associated with this function (with a std::function
wrapper). Then at runtime Source<float>
can be associated with the Dest<double>
and communicate the value to Dest<double>
via a polymorphic pointer (through an inherited interface that all Dest<U>
instances share). Dest<double>
will then invoke the function bar()
using the std::function
wrapper.
So, at runtime, the aim is to associate any Source
with any Dest
, for arbitrary duration, and to re-associate at any time. So I need a way to create a function that can convert a type T
only known at runtime into another type U
also only known at runtime.
To complicate things slightly, the return and parameter values may actually be arrays of arbitrary size. For example, foo()
may return a float[32]
array, but bar()
may expect a double[8]
array (perhaps throwing away 3 of every 4 values). My idea is that this "Bridge" mechanism would handle this, once it can handle the type conversion.
Frankly, I'm not really seeing how to implement this. If the Source-to-Dest associations were fixed at compile time, I could write a family of templated Bridge<T,U>
functions that would do the conversion, but because this all needs to happen at runtime I don't think that's the answer.
Is it the best approach to write a generic Bridge<T,U>
function, then create some sort of dynamic dispatch mechanism to select the appropriate one at runtime based on T and U? How would I go about determining the types of T and U at runtime in order to pick the correct instance of Bridge
? I'd also want to catch the case where T and U are the same type and therefore avoid any unnecessary conversion (as an optimisation).
Or is there a better way to accomplish what I want to do?