Sign in to follow this  
smr

[.net] C# Generics problem

Recommended Posts

This function using a generic parameter will not compile. To my untrained eye it looks as if it should be okay. Any ideas?
        public void Pack<T>(T value) where T: struct
        {
            this.StopPackingBits();
            int insertAt = this.data.Count;
            this.Extend(Marshal.SizeOf(value));

            // Errors on this line:
            this.data.InsertRange(insertAt, BitConverter.GetBytes(value));
        }


Errors: - The best overloaded method match for 'System.BitConverter.GetBytes(bool)' has some invalid arguments - Argument '1': cannot convert from 'T' to 'bool' The GetBytes is defined to accept a bool, so I'm not quite sure what the problem here is.

Share this post


Link to post
Share on other sites
Well, it seems that I can't do this anyway. I was going to need to specialize the Pack method for bool. But it seems that you can't do that with generics. However, I'm still curious why this error is happening as I plan to use generics as often as they make sense.

Share this post


Link to post
Share on other sites
Hi

Generics are cool, aren't they!?

Your problem is that 'System.BitConverter.GetBytes(bool)' is expecting a bool, but you are passing it 'value', which is type T. Type T can be any struct and therefore can be something other than a bool and something not convertible to a bool.

I'm afraid it's not that easy if you're trying to get the bytes of the struct - you'll have to use unmanaged code to cast the struct to a byte array, but I'm not the expert on unmanaged code so I'm afraid I can't give you some sample code. There might have been something on CodeProject.

You could use binary serialization, requiring in tho generic parameter that T must be ISerializable. Unmanaged casting wouldn't work if the struct contained object references, as the raw address would just be copied - that might cause trouble if you unpack the struct again and the garbage collector has moved the referenced object's location in memory.

I would go the serialization route if I were you, less risky and you don't have to use unmanaged code so your program can be run in more environments. But the downside is that you have to make your structs serializable. BTW, if you choose serialization you could remove the struct constraint on T.

Hope that helped, best of luck.

Share this post


Link to post
Share on other sites
Quote:
Original post by DrGUI
Hi

Generics are cool, aren't they!?


They're okay. But I still prefer templates.

Quote:

Your problem is that 'System.BitConverter.GetBytes(bool)' is expecting a bool, but you are passing it 'value', which is type T.


The method GetBytes is overloaded to take just about every simple value type (short, int, long, etc...) including bool. I don't like that because I might pass in a type that the method doesn't accept, using Generics for this function is out the window.

"struct" indicates to the compiler that T must be a value type. It doesn't allow for one to indicate a specific value type such as int. If you want ints, then you have to allow all value types.

Quote:

I'm afraid it's not that easy if you're trying to get the bytes of the struct - you'll have to use unmanaged code to cast the struct to a byte array, but I'm not the expert on unmanaged code so I'm afraid I can't give you some sample code. There might have been something on CodeProject.


This is the route I took. I wasn't too shy about using unmanaged because I wasn't writing to the array of bytes, only reading from it. I was able to check before entering unmanaged if I had enough elements in this.data so that I would have to worry about overstepping its bounds.

Oh, and here's what I did, for those who are interested.

public void Pack(int value)
{
this.StopPackingBits();
int insertAt = this.data.Count;
this.Extend(sizeof(int));

if (insertAt + 4 > this.data.Count)
throw new IndexOutOfRangeException();

unsafe
{
byte* array = (byte*) &value;
this.data[insertAt] = array[0];
this.data[insertAt + 1] = array[1];
this.data[insertAt + 2] = array[2];
this.data[insertAt + 3] = array[3];
}
}

Share this post


Link to post
Share on other sites
If you are interested in using generic structs of differing sizes you can get a pointer to the struct using GCHandle.

For example here is a method I use to copy a generic struct into some binary data block:
public static void Set<T>(T val, IntPtr tgt, int tgtByteOffset) where T: struct {
int stride = Marshal.SizeOf(typeof(T));
T[] vin = new T[]{val};
GCHandle th = GCHandle.Alloc(vin, GCHandleType.Pinned);
Copy8(th.AddrOfPinnedObject(),tgt,tgtByteOffset,0,stride);
th.Free();
}
//and Copy8 is just an unsafe pointer copy:
public static void Copy8(IntPtr src, IntPtr tgt, int srcOffset, int tgtOffset, int count){
unsafe{
byte* tgtptr = ((byte*)tgt)+tgtOffset;
byte* srcptr = ((byte*)src)+srcOffset;
for(int i = 0; i< count; i++){
tgtptr[i] = srcptr[i];
}
}
}



Hope this helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by Tape_Worm
You should pin your arrays before getting their addresses or there is a chance (however small it may be) that the GC will move your array around and thus invalidating your pointer.


Okay. That makes sense. In this situation though I don't think I need to worry about it because the pointer I've aquired is to a value on the stack, so it won't ever be moved by the GC anyway, right?

[Edited by - smr on November 24, 2005 10:17:52 PM]

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