2

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?

davidA
  • 12,528
  • 9
  • 64
  • 96
  • can't you do a dynamic_cast(variable) – TravellingGeek Mar 25 '13 at 02:18
  • 1
    **Code** exhibiting a [SSCCE](http://sscce.org) will go a *long* way in getting feedback on this question. – WhozCraig Mar 25 '13 at 02:29
  • @WhozCraig: I'm working on some examples so I'll see what I can do to post something. – davidA Mar 25 '13 at 02:34
  • @GeraldSv: I was under the impression that dynamic_cast only works with pointers and references? – davidA Mar 25 '13 at 02:36
  • Primitive types are relatively easy. The hard part is non-primitive types -- ie, a type `Egg` that converts to `Chicken`. Some questions: can you have an enumeration every input and output type? Can you round-trip via serialization? Can you write up boilerplate code for each type that you add to the system? (ie, before you can add a type `T` as a `Dest`, you have to register the type `T` somewhere). Can this registration be compile-time deterministic? Or are you adding 3rd party DLLs with their own custom functions that produce `Dest` and `Source`? – Yakk - Adam Nevraumont Mar 25 '13 at 02:59
  • I'm actually mostly concerned with conversion between arrays of floats and doubles, with ints a secondary concern. This is part of a numerical system that is passing around numerical scalars and arrays. The main problem I have with my example attempt so far is in defining a base class for the linkage between the Source and Dest, as the types on both sides are necessary to properly define the 'send' function. I wonder if it might be worth looking into packing the values into a "type anonymous" char array and reinterpreting it on receipt. – davidA Mar 25 '13 at 03:31
  • One idea I'd like to try, but I'm having trouble working out how to do it, is to create and select at runtime from a set of functions: float-array-to-double-array, double-array-to-int-array, etc, but I'm not sure how one can query the type of a Source or Dest (i.e. T and U) at runtime - something like typeid() on a typedef, where the typedef is defined inside the template? – davidA Mar 25 '13 at 03:37
  • I may resort to simply representing everything as a `double`, and then the std::function should handle the conversion down to float, int, etc when calling the final function. Doesn't help with 64-bit integers but I'm not planning to use them anyway. – davidA Mar 25 '13 at 04:07

0 Answers0