I've got a method with the signature
public int Copy(Texture texture, Rect? srcrect, Rect? dstrect)
Rect
is a struct, but I need to allow the caller to pass null
(or IntPtr.Zero
) to the method as well.
I want to then pass it off to a DLL with the signature
[DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_RenderCopy")]
internal static extern int RenderCopy(IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect);
I was hoping I could do something like the following:
return SDL.RenderCopy(_ptr, texture._ptr, srcrect.HasValue ? (IntPtr)srcrect.Value : IntPtr.Zero, dstrect.HasValue ? (IntPtr)dstrect.Value : IntPtr.Zero);
But I can't cast the struct like that. Is there some other way I can get an IntPtr
out of it?
There alternative is to create 4 overloads:
ref Rect, ref Rect
IntPtr, IntPtr
ref Rect, IntPtr
IntPtr, ref Rect
which could get even messier if I ever need to pass more than 2 struct pointers.
I came up with a solution, but I have some questions about it:
public int Copy(Texture texture, Rect? srcrect=null, Rect? dstrect=null)
{
return SDL.RenderCopy(_ptr, texture._ptr, srcrect.HasValue ? StructToPtr(srcrect) : IntPtr.Zero, dstrect.HasValue ? StructToPtr(dstrect) : IntPtr.Zero);
}
private static IntPtr StructToPtr(object obj)
{
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
Marshal.StructureToPtr(obj, ptr, false);
return ptr;
}
Had I used ref Rect
I wouldn't have had to allocate memory for the struct -- what does that do differently than this does?
I did some experimentation. The ref Rect
solution runs at about the same speed as converting a generating an Rect
to an IntPtr
IntPtr
for a Rect
, which leads me to suspect that C# is doing something very similar under the hood when you use refs. As soon as I make it a Rect?
and add the conditional logic to the method it runs up to 50% slower... so the 4-overload route would probably be the fastest. However, we're talking 100-150ms for 100K iterations, which means the method itself is super cheap, which is probably why the conditionals have such a notable impact. As such, I'm sticking with my custom StructToPtr
solution as it's the simplest solution.
An IntPtr is a value type that is primarily used to hold memory addresses or handles. A pointer is a memory address. A pointer can be typed (e.g. int* ) or untyped (e.g. void* ).
When you create a struct object using the New operator, it gets created and the appropriate constructor is called. Unlike classes, structs can be instantiated without using the New operator. If the New operator is not used, the fields remain unassigned and the object cannot be used until all the fields are initialized.
The IntPtr type can be used by languages that support pointers and as a common means of referring to data between languages that do and do not support pointers. IntPtr objects can also be used to hold handles. For example, instances of IntPtr are used extensively in the System.
Yes, you can. The pointer to the class member variable is stored on the stack with the rest of the struct's values, and the class instance's data is stored on the heap.
You want to use Marshal.StructureToPtr
.
You will also have to allocate and deallocate memory for the struct.
A good blog on the subject can be found at http://www.developerfusion.com/article/84519/mastering-structs-in-c/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With