One problem with C# generics is the lack of member constraints. In other words, this is impossible:
public static T Add<T>(T left, T right)
{
    return left + right;
}
People have come up with a large number of solutions, but they all have their own problems, ranging from messy looking syntax to less-than-optimal performance (particularly due to virtual function calls). The solution I propose is a somewhat hacky one (yes that's a word) which maintains both good performance and clean looking code: write the function in IL.

The code for the add method boils down to something like this:
ldarg.0
ldarg.1
add
ret
For those not familiar with IL, the above code adds the first to arguments of the method, then returns the result.

I found that this actually works with generic types. No funny errors, no nothing. Of course, I didn't actually compile any IL source code - I wrote a separate program to generate the assembly. The source code for it is this:
namespace Generator
{
    using System;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;

    public static class Program
    {
        public static void Main()
        {
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Operations"), AssemblyBuilderAccess.Save);
            var module = assemblyBuilder.DefineDynamicModule("Operations", "Operations.dll");
            var type = module.DefineType("Operator", TypeAttributes.Public | TypeAttributes.Sealed);

            type.DefineBinaryOperatorMethod("Add", OpCodes.Add);
            type.DefineBinaryOperatorMethod("Subtract", OpCodes.Sub);
            type.DefineBinaryOperatorMethod("Multiply", OpCodes.Mul);
            type.DefineBinaryOperatorMethod("Divide", OpCodes.Div);
            type.DefineBinaryOperatorMethod("Remainder", OpCodes.Rem);
            type.CreateType();

            assemblyBuilder.Save("Operations.dll");
        }

        private static MethodBuilder DefineBinaryOperatorMethod(this TypeBuilder type, string name, OpCode operation)
        {
            var method = type.DefineMethod(name, MethodAttributes.Public | MethodAttributes.Static);
            var genericParameter = method.DefineGenericParameters("T").First();
            genericParameter.SetBaseTypeConstraint(typeof(ValueType));
            
            method.SetReturnType(genericParameter);
            method.SetParameters(genericParameter, genericParameter);
            method.DefineParameter(1, ParameterAttributes.None, "left");
            method.DefineParameter(2, ParameterAttributes.None, "right");

            var ilGenerator = method.GetILGenerator();
            var continueLabel = ilGenerator.DefineLabel();

            ilGenerator.Emit(OpCodes.Ldtoken, genericParameter);
            ilGenerator.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
            ilGenerator.EmitCall(OpCodes.Callvirt, typeof(Type).GetProperty("IsPrimitive").GetGetMethod(), null);
            ilGenerator.Emit(OpCodes.Brtrue_S, continueLabel);
            ilGenerator.Emit(OpCodes.Ldstr, "The specified type is not supported by this operation.");
            ilGenerator.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) }));
            ilGenerator.Emit(OpCodes.Throw);
            ilGenerator.MarkLabel(continueLabel);
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldarg_1);
            ilGenerator.Emit(operation);
            ilGenerator.Emit(OpCodes.Ret);

            return method;
        }
    }
}
In case your wondering what the ldtoken/call/callvirt/brtrue.s/ldstr/newobj/throw stuff is doing there, that just checks to make sure the type argument is a primitive type (yes this only works on primitive types), and throws an ArgumentException otherwise. Before adding that, .NET threw some pretty nasty exceptions when I tried it on some non-primitive types.

As a final note, the methods generated with indeed work on all primitive types. Even booleans. I was surprised to get a DivideByZeroException upon attempting this:
Operator.Divide(true, false);
Again, this only works on primitive types. Remember, string and decimal are not primitive types. Perhaps this could be extended to calling the op_* functions...

After a couple small performance tests, I found that this method was nearly as fast as doing simple addition (x + y). Then again, I know nothing about microbenchmarks. Try it for yourself :D

Enjoy
1

View comments

  1. Well, I got tired of Blogger :D. New blog here.
    0

    Add a comment

  2. While developing spectral, I discovered something peculiar.

    Consider this sample pseudo C#/IL:
    public static T Sqrt<T>(T value)
    {
        ldarg.0
        conv.r8
        call double System.Math.Sqrt(double)
        ret
    }
    
    You'd expect some sort of error, right? The return type is T, but the returned value is a double.
    Turns out it runs fine. What's more, you don't even need the conv.r8 instruction.

    I have to say, IL's flexibility is amazing.

    YellPika
    0

    Add a comment

  3. I decided to shove my findings in the last post into a library, dubbed Spectral.

    Download the latest change set here.
    0

    Add a comment

  4. One problem with C# generics is the lack of member constraints. In other words, this is impossible:
    public static T Add<T>(T left, T right)
    {
        return left + right;
    }
    
    People have come up with a large number of solutions, but they all have their own problems, ranging from messy looking syntax to less-than-optimal performance (particularly due to virtual function calls). The solution I propose is a somewhat hacky one (yes that's a word) which maintains both good performance and clean looking code: write the function in IL.

    The code for the add method boils down to something like this:
    ldarg.0
    ldarg.1
    add
    ret
    
    For those not familiar with IL, the above code adds the first to arguments of the method, then returns the result.

    I found that this actually works with generic types. No funny errors, no nothing. Of course, I didn't actually compile any IL source code - I wrote a separate program to generate the assembly. The source code for it is this:
    namespace Generator
    {
        using System;
        using System.Linq;
        using System.Reflection;
        using System.Reflection.Emit;
    
        public static class Program
        {
            public static void Main()
            {
                var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Operations"), AssemblyBuilderAccess.Save);
                var module = assemblyBuilder.DefineDynamicModule("Operations", "Operations.dll");
                var type = module.DefineType("Operator", TypeAttributes.Public | TypeAttributes.Sealed);
    
                type.DefineBinaryOperatorMethod("Add", OpCodes.Add);
                type.DefineBinaryOperatorMethod("Subtract", OpCodes.Sub);
                type.DefineBinaryOperatorMethod("Multiply", OpCodes.Mul);
                type.DefineBinaryOperatorMethod("Divide", OpCodes.Div);
                type.DefineBinaryOperatorMethod("Remainder", OpCodes.Rem);
                type.CreateType();
    
                assemblyBuilder.Save("Operations.dll");
            }
    
            private static MethodBuilder DefineBinaryOperatorMethod(this TypeBuilder type, string name, OpCode operation)
            {
                var method = type.DefineMethod(name, MethodAttributes.Public | MethodAttributes.Static);
                var genericParameter = method.DefineGenericParameters("T").First();
                genericParameter.SetBaseTypeConstraint(typeof(ValueType));
                
                method.SetReturnType(genericParameter);
                method.SetParameters(genericParameter, genericParameter);
                method.DefineParameter(1, ParameterAttributes.None, "left");
                method.DefineParameter(2, ParameterAttributes.None, "right");
    
                var ilGenerator = method.GetILGenerator();
                var continueLabel = ilGenerator.DefineLabel();
    
                ilGenerator.Emit(OpCodes.Ldtoken, genericParameter);
                ilGenerator.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
                ilGenerator.EmitCall(OpCodes.Callvirt, typeof(Type).GetProperty("IsPrimitive").GetGetMethod(), null);
                ilGenerator.Emit(OpCodes.Brtrue_S, continueLabel);
                ilGenerator.Emit(OpCodes.Ldstr, "The specified type is not supported by this operation.");
                ilGenerator.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) }));
                ilGenerator.Emit(OpCodes.Throw);
                ilGenerator.MarkLabel(continueLabel);
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldarg_1);
                ilGenerator.Emit(operation);
                ilGenerator.Emit(OpCodes.Ret);
    
                return method;
            }
        }
    }
    
    In case your wondering what the ldtoken/call/callvirt/brtrue.s/ldstr/newobj/throw stuff is doing there, that just checks to make sure the type argument is a primitive type (yes this only works on primitive types), and throws an ArgumentException otherwise. Before adding that, .NET threw some pretty nasty exceptions when I tried it on some non-primitive types.

    As a final note, the methods generated with indeed work on all primitive types. Even booleans. I was surprised to get a DivideByZeroException upon attempting this:
    Operator.Divide(true, false);
    Again, this only works on primitive types. Remember, string and decimal are not primitive types. Perhaps this could be extended to calling the op_* functions...

    After a couple small performance tests, I found that this method was nearly as fast as doing simple addition (x + y). Then again, I know nothing about microbenchmarks. Try it for yourself :D

    Enjoy
    1

    View comments

    1. This is a great function to use, it allows better design of generics.

      I found that it is takes about 8 times longer to do the addition followed by subtraction operation.

      I wonder if any of the overhead is due to calling the operator.dll? Could it be faster if every dll that uses the code contains the operator code directly? i.e. some kind of "in-line" function?

      int steps = 100000000;
      int A = 0;
      Stopwatch sw = Stopwatch.StartNew();
      for(int i = 0; i < steps; i++)
      {
      A = A + i;
      A = i - A;
      }
      sw.Stop();
      A = 0;
      Stopwatch sw2 = Stopwatch.StartNew();
      for (int i = 0; i < steps; i++ )
      {
      A = Operator.Add(i, A);
      A = Operator.Subtract(A, i);
      }
      sw2.Stop();
      Console.WriteLine("Normal operator = {0} milliseconds, result={1}", sw.ElapsedMilliseconds, A);
      Console.WriteLine("Normal operator = {0} milliseconds, result={1}", sw2.ElapsedMilliseconds, A);
      Console.WriteLine("Operator takes {0} times as long as ordinary method.", (double)sw2.ElapsedMilliseconds / (double)sw.ElapsedMilliseconds);
      return;

      ReplyDelete
  5. A very funny thing happened today.

    I believe in some language or another (can't remember which), the Abs function is actually a member function - x.abs() instead of abs(x). It also means you can do something like -1.abs(). I wanted to try to emulate that sort of thing with extensions methods. So, I wrote this:

    public static int Abs(this int value)
    {
        return Math.Abs(value);
    }
    

    Simple enough. So I'm writing code to find the determinant of a matrix (via Expansion by Minors), and the return values are coming out all wrong. Sometimes they were right, sometimes they were the wrong sign, and sometimes they were way off. I checked the code a hundred times until I came back to this:

    for (int i = 0; i < matrix.Size; i++)
        output += matrix[0, i] * matrix.Minor(0, i) * -1.Pow(i);
    

    Looks fine at first glance... to double check, I put each part of the equation into their own variables. During debug mode... -1.Pow(0) = -1!? That's not right!! Oh wait...

    for (int i = 0; i < matrix.Size; i++)
        output += matrix[0, i] * matrix.Minor(0, i) * (-1).Pow(i);
    

    ...problem solved :P

    I now see why abs(x) is better than x.abs()
    0

    Add a comment

  6. Something wonderfully terrible happened today: I got reviewed.
    A negative review, yes. But also a very stupid one.

    The reviewer complains that the s/he can't compile the source because it won't load in VS2010.
    Keep in mind this is a somewhat old game that I made quite some time ago, using... XNA 3.0.

    And of course, v3.0 was for VS2008... does not, and never will, work with 2010...


    On the bright side, I now have all the motivation I need to create a new version of the game.

    YellPika
    0

    Add a comment

  7. This has been done a thousand times, but here goes anyway...
    Ok, sort of. After much experimenting, this is the simplest method I could come up with.

    This method uses linear depth, and reconstructs the position in world space.
    To store the position:

    /* Vertex Shader */
    float4 viewPosition = mul(input.Position, mul(World, View));
    output.Depth = (-viewPosition.z - NearPlane) / (FarPlane - NearPlane);
    
    /* Pixel Shader */
    output.Depth = input.Depth; // Um... yeah... just output the depth...
    

    To reconstruct the position, we're going to interpolate between the camera's frustum corners.
    So:

    /* Your Code (C#) */
    BoundingFrustum frustum = new BoundingFrustum(view * projection);
    effect.SetParameter["FrustumCorners"].SetValue(frustum.GetCorners());
    

    /* Screen Space Pixel Shader */
    float depthSample = tex2D(DepthTextureSampler, screenCoord);
    
    float3 position = lerp(
        lerp(
            lerp(FrustumCorners[0], FrustumCorners[1], screenCoord.x),
            lerp(FrustumCorners[3], FrustumCorners[2], screenCoord.x),
            screenCoord.y),
        lerp(
            lerp(FrustumCorners[4], FrustumCorners[5], screenCoord.x),
            lerp(FrustumCorners[7], FrustumCorners[6], screenCoord.x),
            screenCoord.y),
        depthSample);
    

    So there you have it.
    1

    View comments

  8. Say you have a simple class which implements the singleton pattern.



    Now you want to create a class which inherits the singleton functionality.



    So, you call Derived.GetInstance() and... you get an instance of a Singleton, not a Derived!!

    An interesting (and somewhat wacky) solution to this problem:
    The Curiously Reccuring Template Pattern



    Trust me, it compiles. Now, calling Derived.GetInstance() returns an instance of Derived, not Singleton. Hooray for CRTP!!

    One problem though - you can't do this:



    Definitions of Singleton must have type arguments defined. This can be worked around by implementing an interface:

    0

    Add a comment

  9. In an effort to emulate Reactive Programming, I have created this nifty little class. It's preliminary (created five minutes ago), but seems useful enough for now.

    January 17, 2011:
    I've decided to make the structure immutable. The type is meant to be used as a property, so any time the value is retrieved, a copy would be returned. A call to BindValue() would screw the whole thing over.

    ALSO:
    Fixed an embarassingly dumb error in GetValue().

    January 19, 2011:
    Just realized that most of my generic arguments magically became lower case.

    0

    Add a comment

  10. Every once in a while, I run into this sort of ugly situation:

    public MyClass(GraphicsDevice graphicsDevice)
        : this(graphicsDevice, graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height)
    { }


    public MyClass(GraphicsDevice graphicsDevice, int width, int height)
    {
        if (graphicsDevice == null)
            throw new ArgumentNullException("graphicsDevice");


        // etc.
    }

    I just want to do a basic check. Throw an exception if graphicsDevice is null. Simple enough. So what happens when I call the overload? Since the overload calls the Viewport property of the graphicsDevice, a NullReferenceException will be thrown instead of an ArgumentNullException, which is considerably less informative to the end user. The obvious solution is to not call the other constructor, but that leads to duplicated code. How am I supposed to solve this one?

    Code Contracts. The above now becomes this.

    public MyClass(GraphicsDevice graphicsDevice)

        : this(graphicsDevice, graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height)
    {
        Contract.Requires<NullReferenceException>(graphicsDevice != null);
    }

    public MyClass(GraphicsDevice graphicsDevice, int width, int height)
    {
        Contract.Requires<NullReferenceException>(graphicsDevice != null);

        // etc.
    }

    Since Code Contracts modifies code at compile time, the contract will actually be executed before the other constructor is called. I'm still duplicating some code. But it's better than duplicating the entire constructor.

    Now if only I could get static checking without having to pay extra for it...
    0

    Add a comment

Blog Archive
Active Projects
Active Projects
Total Pageviews
Total Pageviews
10814
Loading