Do structs add any overhead to instance size?
Asked Answered
P

2

9

Specifically, if I make a struct that has a single field, that essentially acts as a wrapper around that value, is it safe to pass this struct to a P/Invoke method expecting the underlying type?

I'm working with a native library whose API involves a lot of pointer-to-struct types, and I'd like to use something a bit more typesafe than IntPtr to keep them all straight, by wrapping IntPtr in a generic struct. Would that work? (And has it already been done?)

Pelagia answered 27/7, 2015 at 10:56 Comment(4)
struct is just a blob in-memory, nothing more. It doesn't have any additional overhead other then the members of the struct itself. You can even specify the way you want to layout the struct in memory using StructLayoutAttribute.Transcendence
Can you show a signature that you want to alter and how? Do you mean you want to change SomeFunction(IntPtr foo) to SomeFunction(MyStruct foo)?Gorden
@CodeCaster: ref MyStruct foo would be ideal, except that I get the MyStruct from the external library, from a function that returns a MyStruct*.Pelagia
Are you still stuck?Shoring
S
7

So long as you use LayoutKind.Sequential, then the struct's single field will be located at offset zero. Therefore, your assumption is correct.

I presume that by pointer-to-struct type, you mean an opaque pointer. The struct is forward declared but never defined. However, in that case I cannot see how declaring a struct in your C# code can really help you. An opaque pointer is, well, opaque. Only the library implementation knows how to allocate it. You can't allocate it in the consumer.

Update

Perhaps you want to wrap an opaque pointer in a struct like this:

[StructLayout(LayoutKind.Sequential)]
struct FooHandle
{
    IntPtr handle;
}

// and so on for other handle types

Interop with this struct will be indistinguishable from interop with IntPtr.

Shoring answered 27/7, 2015 at 11:4 Comment(7)
And since it may not be obvious to the OP, LayoutKind.Sequential is the default for structs (according to the documentation: "C#, Visual Basic, and C++ compilers apply the Sequential layout value to structures by default."), so if nothing is specified, nothing needs to be added.Boycott
You can't allocate it in the consumer. I can receive it in the consumer from the library implementation, and define the PInvoke method that returns it as returning a TypedPointer<LibraryStruct>, which is much easier to work with than a IntPtr.Pelagia
@Mason I'm having to guess a little as to what your scenario is, and what your proposed solution looks like.Shoring
The scenario is that I've got a native library that defines a bunch of struct types, and its API uses pointers to them all over the place. It allocates and frees them internally. This works just fine in native code land, but if you try to PInvoke into it, suddenly everything becomes an IntPtr, which is essentially equivalent to void*. All your type safety and the ability to tell types apart by name goes right out the window, and the code that uses this library becomes very messy.Pelagia
I can't see how declaring fake structs in C# will help your actual problem, but I think I answered the question you asked.Shoring
Ah, I see now. The member of the struct will be a single IntPtr?Shoring
@DavidHeffernan ExactlyPelagia
S
7

Don't pass it as a struct, then.

Instead of figuring out how to do this directly with P/Invoke, just keep the P/Invoke methods private, and expose public methods that will take your wrapper type directly - and handle whatever you need to do to pass it forward. Your code is then only going to use these public methods without having to worry about unsafe structures :)

Mixing different types and expecting them to magically work is a recipe for disaster. Make yourself explicit, and don't rely on hiding and implicit forced casts.

The best thing about this is that it allows you to very easily use some pointer wrapper that is actually safe - you can keep it as a reference, and if it's ever lost, you can use a finalizer to dispose of the unmanaged resource as needed. Have a look at how SafeHandle and it's descendants work for a great sample.

Shandra answered 27/7, 2015 at 11:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.