Sign in to follow this  
Spa8nky

[C#] Can't get the following generics based function to work.

Recommended Posts

Spa8nky    230
I'm looking to implement a function that can take in an int, byte, short, float, double, etc. and wrap that number around a particular range. The following doesn't work, as T can't be converted. What have I done wrong?
        public T Wrap(T value, T min, T max)
        {
            return ((value - min) % (max - min + 1)) + min;
        }
The gentleman who posted originally posted the code (Zipster) used the following for C++:
public T Wrap<T>(T value, T min, T max)
{
   return ((value - min) % (max - min + 1)) + min;
}
He stated that if the value was less than min then the function did not work as intended. Can anyone suggest an alternative method for wrapping a number, and help with my generics problem with my C# function please? Thank you.

Share this post


Link to post
Share on other sites
Codeka    1239
C# does not have "duck typing" (which is how the template version works in C++). Basically, you can't do it (at least, I can't think of a way) and you'll just have to provide the overloads manually.

Share this post


Link to post
Share on other sites
Telastyn    3777
The problem is that C# generics constrain what you can do with them. In that example, you specify no constraint, so the compiler knows they are (at least) of type object. You can't add or modulo object so it complains.

Unfortunately, there's no 'numeric' constraint. Your best bet would be to constrain them to IComparable and use a different algorithm to constrain the value.

Share this post


Link to post
Share on other sites
Fenrisulvur    186
"Duck typing" has to be one of the most obscure (and therefore hilarious) names for a programming concept I've seen in...probably three days. I love programming. :)

Obvious work-around if you're implementing the types yourself: declare a Wrappable interface.

However, there's a far more awesome way:

        public static T Wrap<T>(T value, T min, T max)
{
Type[] TTParams = new Type[] { value.GetType(), value.GetType() };
Type[] TIntParams = new Type[] { value.GetType(), typeof(Int32) };

System.Reflection.MethodInfo plusT =
value.GetType().GetMethod("op_Addition", TTParams);

System.Reflection.MethodInfo plusInt =
value.GetType().GetMethod("op_Addition", TIntParams);

System.Reflection.MethodInfo minusT =
value.GetType().GetMethod("op_Subtraction", TTParams);

System.Reflection.MethodInfo modT =
value.GetType().GetMethod("op_Modulus", TTParams);

// (value - min)
T temp1 = (T)minusT.Invoke(null, new object[] { value, min });

// (max - min + 1)
T temp2 = (T)minusT.Invoke(null, new object[] { max, min });
temp2 = (T)plusInt.Invoke(null, new object[] { temp2, 1 });

// ((value - min) % (max - min + 1))
T temp3 = (T)modT.Invoke(null, new object[] { temp1, temp2 });

// return ((value - min) % (max - min + 1)) + min;
return (T)plusT.Invoke(null, new object[] { temp3, min }); ;
}


I'm completely unfazed by (albeit interested in, particular where corroboration is provided) anything anyone has to say about performance on this one. I'm not a C# programmer, this baby took effort for me to write. >_>

Oh, and you'll get C#'s brand of null reference exception if you feed this method a data type that doesn't implement - and % for itself, and + for itself and Int32.

Oh, and I'm now cheating on Java. I feel kinda guilty.

Share this post


Link to post
Share on other sites
cache_hit    614
It seems like every day, a new example surfaces of why I hate C# generics.

If they had a different name I'd like them better, but they are anything but generic.

Share this post


Link to post
Share on other sites
Telastyn    3777
Quote:
Original post by cache_hit
It seems like every day, a new example surfaces of why I hate C# generics.

If they had a different name I'd like them better, but they are anything but generic.


You should look at Java's generics then. C#'s are wonderful in comparison.

[edit:]
And reflection invoked math ops are dog slow. You'd be better off using dynamics. I used reflection for Tangent's interface to math ops, and its essentially what killed my motivation. They're literally about 10,000 times slower than the equivalent cil instruction.

Share this post


Link to post
Share on other sites
Spodi    642
C# generics aren't bad, they just aren't always what you need completely. I would love to have both C# generics and C++ templates for places (like this) where generics just doesn't cut it.

Anywho, the problem of "take an arbitrary type and perform these operations on it" is still possible, just not with generics alone. Enter the world of Dynamic Methods:


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NUnit.Framework;

namespace ConsoleApplication1
{
[TestFixture]
public class Program
{
static void Main(string[] args)
{
const int it = 100000000;

int n, d;
Benchmark(it, out n, out d);
Console.WriteLine("Iterations: {0}", 1000000);
Console.WriteLine("Static: {0}", n);
Console.WriteLine("Dynamic: {0}", d);

Console.Read();
}

static void Benchmark(int iterations, out int nondynamic, out int dynamic)
{
var dwrap = CreateWrap<int>();

Stopwatch w = new Stopwatch();
w.Start();
for (int i = 0; i < iterations; i++)
Wrap(5, 0, 10);
w.Stop();
nondynamic = (int)w.ElapsedMilliseconds;
w.Reset();
w.Start();
for (int i = 0; i < iterations; i++)
dwrap(5, 0, 10);
w.Stop();
dynamic = (int)w.ElapsedMilliseconds;
}

[Test]
public void WrapIntTest()
{
var dynamicWrap = CreateWrap<int>();
Assert.AreEqual(Wrap(5, 1, 100), dynamicWrap(5, 1, 100));
}

[Test]
public void WrapFloatTest()
{
var dynamicWrap = CreateWrap<float>();
Assert.AreEqual(Wrap(5f, 1f, 100f), dynamicWrap(5, 1, 100));
}

static int Wrap(int value, int min, int max)
{
return ((value - min) % (max - min + 1)) + min;
}

static float Wrap(float value, float min, float max)
{
return ((value - min) % (max - min + 1)) + min;
}

static Func<T, T, T, T> CreateWrap<T>()
{
var pValue = Expression.Parameter(typeof(T), "value");
var pMin = Expression.Parameter(typeof(T), "min");
var pMax = Expression.Parameter(typeof(T), "max");

var valueMinusMin = Expression.Subtract(pValue, pMin);
var maxMinusMin = Expression.Subtract(pMax, pMin);
var maxMinusMinPlus = Expression.UnaryPlus(maxMinusMin);
var mod = Expression.Modulo(valueMinusMin, maxMinusMinPlus);
var finalAdd = Expression.Add(mod, pMin);

return Expression.Lambda<Func<T, T, T, T>>(finalAdd, new[] { pValue, pMin, pMax }).Compile();
}
}
}




(Updated with benchmarks, since I figured the next thing to come out of someone's mouth will involve performance)

If you define something that is not supported (does not implement +, -, %, and ++) then you will get an InvalidOperationException when calling CreateWrap<>(). Otherwise, it'll produce pretty much the same code as:

        static T X(T value, T min, T max)
{
var a = value - min;
var b = max - min;
b++;
var c = a % b;
return c + min;
}



Performance-wise, its nearly the same. Dynamic methods are slightly slower than actual code, but its still pretty damn impressive that it is so close.

Share this post


Link to post
Share on other sites
Telastyn    3777
You just get the overhead of the delegate invocation, which is what? 1.5-3 times worse in normal usage? Not too bad.

Doing this straight with 4.0 dynamics should be speedier than that even.

Share this post


Link to post
Share on other sites
Washu    7829
Quote:
Original post by Telastyn
You just get the overhead of the delegate invocation, which is what? 1.5-3 times worse in normal usage? Not too bad.

Doing this straight with 4.0 dynamics should be speedier than that even.

Not really, the behavior of dynamics is to do a runtime lookup of the appropriate methods. It's essentially reflection in disguise. Don't expect dynamic to be some amazing performance band aid for such code, it's not.

Share this post


Link to post
Share on other sites
ChaosEngine    5185
Getting back to the original question, why isn't there an INumeric interface? Seems like a glaring oversight from the pov of generics.

To the OP, I'd just provide int and double overloads. short, byte and float can upcast.

Share this post


Link to post
Share on other sites
Spa8nky    230
Thank you for this useful insight into generics guys. I'm thinking overloads are probably going to be a safer bet for C#!

Has anyone got any interesting wrap methods they would like to share?

Share this post


Link to post
Share on other sites
Telastyn    3777
Quote:
Original post by ChaosEngine
Getting back to the original question, why isn't there an INumeric interface? Seems like a glaring oversight from the pov of generics.


How do you define infix operators on an interface?

Quote:

Not really, the behavior of dynamics is to do a runtime lookup of the appropriate methods. It's essentially reflection in disguise. Don't expect dynamic to be some amazing performance band aid for such code, it's not.


Sure, the first invocation is essentially (worse than) reflection. When I did some (admittedly artificial) benchmarking of the DLR's method caching a few months back, it was decidedly better than even delegate invocation over many calls for this sort of basic math ops.

I would be surprised if plain old dynamic invocation wasn't at least as performant as spodi's example, while being far more legible.

Share this post


Link to post
Share on other sites
Zipster    2365
Seeing as how I'm partly to blame for this thread, I apologize for posting broken code, to be honest I was mentally "porting" from the C++ version over the the C# version and figured that generics were the closest analog to templates and that they would work roughly the same, despite my lack of in-depth experience working with my own generic types [embarrass] So I'll just keep going in C++ and leave it as an exercise to the read to do the porting [wink]

Quote:
Original post by Spa8nky
Has anyone got any interesting wrap methods they would like to share?

I would probably just split it into two cases, value < min and value ≥ min:
template<typename T>
T Wrap(T value, T min, T max)
{
// This part is up to you, if not a precondition
if(max < min)
std::swap(min, max);

const T count = max - min + static_cast<T>(1);
const T offset = (value < min) ? (value - min + static_cast<T>(1)) : (value - min);
const T origin = (value < min) ? max : min;

return (offset % count) + origin;
}
I barraged it with a bunch of values and ranges and it appeared to work great. Integral types only, of course, so you'll need specializations for floating-point types.

Share this post


Link to post
Share on other sites
Telastyn    3777
Because I'm feeling mildly evil... (requires .NET 4)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {
public static class NumericExtension {
public static T FixToRange<T>(this T value, T min, T max){
return (fixToRange(value, min, max));
}

private static dynamic fixToRange(dynamic value, dynamic min, dynamic max) {
if (value < min) {
return (min);
}
if (max < value) {
return (max);
}
return (value);
}
}

class Program {
static void Main(string[] args) {
int x = 4;
double i = 3.14;

Console.WriteLine(x.FixToRange(0, 2));
Console.WriteLine(i.FixToRange(3, 3.5));
Console.WriteLine(4.FixToRange(0, 6));
Console.WriteLine(3.14.FixToRange(0, 3));
}
}
}






2
3.14
4
3


And there you are. One dynamic extension method to fix arbitrary things to ranges (and throw exceptions for things that doesn't implement less-than). The actual implementation probably should get argument checking.

[Edited by - Telastyn on February 16, 2010 7:16:22 AM]

Share this post


Link to post
Share on other sites
Mike.Popoloski    3258
The use of dynamic there is completely unnecessary.


public static T Clamp(T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0)
return min;
if (value.CompareTo(max) > 0)
return max;

return value;
}

Share this post


Link to post
Share on other sites
Codeka    1239
Quote:
Original post by Mike.Popoloski
The use of dynamic there is completely unnecessary.
I should just point out that this is not what the OP was asking for... it was supposed to be a "wrap" operation, not a "clamp" operation, and I can't see a way to do "wrap" with generics.

Share this post


Link to post
Share on other sites
Spa8nky    230
Quote:

it was supposed to be a "wrap" operation, not a "clamp" operation, and I can't see a way to do "wrap" with generics.


Generics and wrapping seem completely out of reach for me so I am happy to overload any non generics method you guys care to share also.

Share this post


Link to post
Share on other sites
Telastyn    3777
Quote:
Original post by Mike.Popoloski
The use of dynamic there is completely unnecessary.


To be pedantic, IComparable and 'implements operator<' are different criteria. string for example works in one and not the other. It's also important for any sort of situation where you want a 'INumeric' sort of behavior.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this