Thursday, 6 March 2014

After a quite good amount of work on the User interface (more post to follow on this part), I decided to go back into the inner tech of things, and try to find some different approaches.

One important thing when you are a user and you use graphical programming, you need to connect elements. With class (reference types), this is rather straightforward, you use Type.IsAssignableFrom and you're more or less done.

Now let's have a bit more interest into value types.

In that case the above (of course) doesn't work. let's make a very simple Pin model:

Code Snippet

publicclassInputPin<T>

{

private T data;

public T Data

{

get { returnthis.data; }

internalset

{

{ this.data =value; }

}

}

}

publicclassOutputPin<T>

{

public T Data { get; set; }

}

As you see, nothing fancy.

Now let's say we have those :

Code Snippet

var input =newInputPin<double>();

var output =newOutputPin<float>();

They of course can't natively connect (Type.IsAssignable) with not work. But in c#:

Code Snippet

float f =150.0f;

double d = f;

This is perfectly valid, so there must be a way.

So how to handle connections in that case?
We have a few cases that we can thing of:

This is a native type

The type already implements either implicit or explicit

We want to do it ourselves and provide our own.

I'll start by option 2 (will explain the reason below).

By reflection, to check if a type is assignable to another one, this is what we do instead:

Code Snippet

privateMethodInfo FindMethod(Type t)

{

return t.GetMethods().Where(

m => m.Name =="op_Implicit"

&& m.ReturnType ==typeof(TTarget)

&& m.GetParameters().Count() ==1

&& m.GetParameters()[0].ParameterType == t).FirstOrDefault();

}

If we have a MethodInfo, we are able to invoke this and convert our type.

So after we can easily build a small IL Generator that builds either a Func or a small class wrapping instances. In each case this is simple:

EmitGetter and Setter just take care of automatic property/field to choose a CallVirt/LdFld operator.

Now from that you can simply do :

Code Snippet

var input =newInputPin<double>();

var output =newOutputPin<float>();

output.Data =154.0f;

var dbl =newDoubleNativeTypeConverter();

var gen = dbl.CreateGenerator<OutputPin<float>, InputPin<double>>(

Select<OutputPin<float>, float>(tp => tp.Data),

Select<InputPin<double>, double>(t => t.Data)

);

var act = gen.CreateAction();

act(output, input);

The BIG difference, you can do all this at runtime, with reflection. No need to build two million converters.

Of course an acute reader would notice, I have : DoubleNativeTypeConverter

Yes basic primitive types (as of double, int, float...) don't expose this to reflection, so in that case you have to implement this manually (doing this for 20 types instead of thousands is a trade off I happily take, and in those cases you can simply use the Conv_(Type) opcode and just build a list of supported types, not a biggie.

Please note that in this case I emit a Func (eg: a dynamic method), which in some cases is not always what you want (If your converter needs to hold resources for example, you'd need to generate a class with IDisposable, wich would just have an "update function'". You could also simply register this class an write it by hand. In neither case it's an issue.

Possibilities are a bit endless, more on that in the next post (which might be a little while due to my project workload)...