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?