Let's say I have a struct that's not one of the C# primitives.
I'd like to know if there's a way (including unsafe code) to get a pointer from an instance of the struct type (the pointer doesn't have to be type-safe, it could be void*) and get an instance of the struct type from a pointer, but without boxing involved.
(I know that the System.Runtime.InteropServices.Marshal class offers almost what I'm describing, but it involves passing and getting structs as object, which means that boxing is involved.)
You can use an unsafe region to get the pointer using the & operator and convert back to the type using the * operator.
public struct Foo
{
public int bar;
};
void Main()
{
var foo = new Foo();
foo.bar = 5;
unsafe
{
Foo * fooPtr = &foo;
Console.WriteLine("Foo address {0:X}", *fooPtr);
Foo anotherFoo = *fooPtr;
Console.WriteLine("Bar= {0}", anotherFoo.bar);
}
}
I don't quite understand what is the problem with taking the address of the struct with & operator. If you're not aware of that, there exist address of operator.
For example:
struct Demo
{
public int SomeField;
}
internal static class Program
{
static Demo demo = new Demo { SomeField = 5 };
private static void Main()
{
Print();
}
public static unsafe void Print()
{
fixed (Demo* demop = &demo)
{
Console.WriteLine(demop->SomeField);
}
}
}
This code doesn't boxes the struct, though it needs unsafe context to compile.
Related
I'm working on a project that uses both C++ and C#.
However, when passing C#, i get a copy instead of the actual object, which doesn't work for me because i need to read and write to it both on on C# and C++.
on C++ the class looks like this:
class TestClass {
public:
float f1;
float f2;
}
whereas on C# the class looks like this
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x8)]
public class TestClass {
[FieldOffset(0x0)] public float f1;
[FieldOffset(0x4)] public float f2;
}
then the way i read it in C# is by doing this:
public class TestProgram {
public static CTestClass Test;
public static delegate void SetTestDelegate(IntPtr pTest);
public static void SetTest(IntPtr pTest) {
if (pTest == IntPtr.Zero)
return;
object? TestObj = Marshall.PtrToStrucutre(pTest, typeof(TestClass))
if (TestObj == null)
return;
Test = (TestClass)TestObj;
}
}
the SetTest method gets invoked on C++ like this:
typedef void (*tSetTest) (TestClass* pClass):
void SetNetTest() {
// code that gets the library path and stuff here
tSetTest SetTest;
hostfxr_load_assembly(wsLibPath.c_str(), L"TestProgram, testlib", L"SetTest", L"TestProgram+SetTestDelegate, testlib", nullptr, (void**)&SetTest);
TestClass* pTestClass = new TestClass();
SetTest(pTestClass);
}
but this just returns a copy of the object at the time of reading it rather than an actual instance of it.
is there any way i can change that without using unsafe code or writing setters/getters in C++?
Why would the inline instantiated struct suddenly become a managed type? Not only is its generic constrained to unmanaged, but the struct itself is fully blittable.
public struct MyStruct<T> where T : unmanaged
{
public int SomePrimitive;
}
public void DoSomething()
{
var mystruct = new MyStruct<int>();
var myPtr = &mystruct;
}
I was using C# 7.3 (Unity Compatibility). Apparently this exact thing became possible in C# 8.0
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#unmanaged-constructed-types
Usually, treating a struct S as an interface I will trigger autoboxing of the struct, which can have impacts on performance if done often. However, if I write a generic method taking a type parameter T : I and call it with an S, then will the compiler omit the boxing, since it knows the type S and does not have to use the interface?
This code shows my point:
interface I{
void foo();
}
struct S : I {
public void foo() { /* do something */ }
}
class Y {
void doFoo(I i){
i.foo();
}
void doFooGeneric<T>(T t) where T : I {
t.foo(); // <--- Will an S be boxed here??
}
public static void Main(string[] args){
S x;
doFoo(x); // x is boxed
doFooGeneric(x); // x is not boxed, at least not here, right?
}
}
The doFoo method calls foo() on an object of type I, so once we call it with an S, that S will get boxed. The doFooGeneric method does the same thing. However, once we call it with an S, no autoboxing might be required, since the runtime knows how to call foo() on an S. But will this be done? Or will the runtime blindly box S to an I to call the interface method?
void doFooGeneric<T>(T t) where T : I {
t.foo(); // <--- Will an S be boxed here??
}
Boxing will be avoided there!
The struct type S is sealed. For value type versions of the type parameter T to your method doFooGeneric above, the C# compiler gives code that calls the relevant struct member directly, without boxing.
Which is cool.
See Sameer's answer for some technical details.
OK, so I came up with an example of this. I will be interested in better examples if anyone has some:
using System;
using System.Collections.Generic;
namespace AvoidBoxing
{
static class Program
{
static void Main()
{
var myStruct = new List<int> { 10, 20, 30, }.GetEnumerator();
myStruct.MoveNext(); // moves to '10' in list
//
// UNCOMMENT ONLY *ONE* OF THESE CALLS:
//
//UseMyStruct(ref myStruct);
//UseMyStructAndBox(ref myStruct);
Console.WriteLine("After call, current is now: " + myStruct.Current); // 10 or 20?
}
static void UseMyStruct<T>(ref T myStruct) where T : IEnumerator<int>
{
myStruct.MoveNext();
}
static void UseMyStructAndBox<T>(ref T myStruct)
{
((IEnumerator<int>)myStruct).MoveNext();
}
}
}
Here the type of myStruct is a mutable value-type which holds a reference back to the List<>, and also holds "counter" that remembers what index in the List<> we have reached until now.
I had to use ref, otherwise the value-type would be copied by value when passed into either of the methods!
When I uncomment the call to UseMyStruct (only), this method moves the "counter" inside our value type one position ahead. If it did that in a boxed copy of the value type, we would not see it in the original instance of the struct.
To see what the difference is with boxing, try the call UseMyStructAndBox instead (comment UseMyStruct again). It creates a box at the cast, and the MoveNext happens on a copy. So the output is different!
To those who are unhappy with (or confused by) the ref, just write out Current from within the method instead. Then we can get rid of ref. Example:
static void F<T>(T t) where T : IEnumerator<int>
{
t.MoveNext(); // OK, not boxed
Console.WriteLine(t.Current);
}
static void G<T>(T t) where T : IEnumerator<int>
{
((IEnumerator<int>)t).MoveNext(); // We said "Box!", it will box; 'Move' happens to a copy
Console.WriteLine(t.Current);
}
Boxing will be avoided as Constrained Opcodes come to play in the second case.
In my c# application i receive pointer to c++ struct in callback/delegate. I'm not sure if class can do the trick but just casting c++ pointer to appropriate c# struct works fine, so I'm using c# struct for storing data.
Now I want to pass reference to struct for further processing
I can't use class because it probably will not "map" perfectly to c++ struct.
I don't want to copy struct for better latency
How can I do that?
This example demonstrates that struct is passed by value, not by reference:
using System;
namespace TestStruct
{
struct s
{
public int a;
}
class Program
{
static void Main(string[] args)
{
s s1 = new s
{
a = 1
};
Foo(s1);
Console.WriteLine("outer a = " + s1.a);
}
private static void Foo(s s1)
{
s1.a++;
Console.WriteLine("inner a = " + s1.a);
}
}
}
Output is:
inner a = 2
outer a = 1
It sounds like you just want to use ref to pass the struct by reference:
private static void Foo(ref s s1)
{
s1.a++;
Console.WriteLine("inner a = " + s1.a);
}
And at the call site:
Foo(ref s1);
See my article on parameter passing in C# for more details.
Note that other than for interop, I would normally strongly recommend against using mutable structs like this. I can understand the benefits here though.
I have this code in C++
class MyClass { ... };
typedef MyClass (*Callback)();
Callback theCB;
static void RegisterCallback( Callback cb ) { theCB = cb; };
static void CallCallback() {
MyClass obj = theCB();
}
I am using swig but for simplicity (if you don't know swig) I have this wrapper in C#
public class MyClassWrapper
{
public IntPtr ptrToNativeObj; // pointer to native MyClass object
public MyClassWrapper()
{
ptrToNativeObj = call to native code that creates
and returns a new instance of MyClass in C++
}
};
Now I want to support the callback mechanism in C# so I set it up like this:
public MyClassWrapper MyFunction()
{
return new MyClassWrapper();
}
delegate MyClassWrapper CallbackDotNet();
static void main()
{
var fct = new CallbackDotNet( MyFunction );
P/Invoke call to native function RegisterCallback( fct );
then finally:
P/Invoke call to native function CallCallback();
}
I have all this code setup to work properly.
The native code in CallCallback will call MyFunction properly.
But now I need to handle the returned object properly...
MyFunction returns a C# reference while the callback in C++ is returning by value so this would not work for free:
static void CallCallback() {
MyClass obj = theCB();
}
How can I marshall the "reference" to a MyClassWrapper object, returned from MyFunction, so that C++ receives "by-value" a MyClass object ?
Should I go ahead and write a custom marshaller ?
http://msdn.microsoft.com/en-us/library/zk0a8dea(v=vs.90).aspx
Then use it like here
[return: MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType = "MyCustomMarshaler")]
delegate MyClassWrapper CallbackDotNet();
I looked at the documentation for custom marshallers and it's quite complex.
Looks like the interesting method to implement is the following:
IntPtr MarshalManagedToNative( Object ManagedObj );
And the code will be something like
IntPtr MarshalManagedToNative( Object ManagedObj )
{
MyClassWrapper val = ManagedObj as MyClassWrapper;
return val.ptrToNativeObj;
}
But this will return a MyClass* back to the native code, not a MyClass value that this C++ code expects !
static void CallCallback() {
MyClass obj = theCB();
}
Will the marshaller be smart enough to dereference the pointer ?
Thank you all for your comments
Looks like the custom marshaler is the way to go !
I did a simple test case and everything works fine. It works as expected.
Here is the marshaler in case you are interested:
public class MyCustomMarshaler : ICustomMarshaler
{
public static ICustomMarshaler GetInstance(String cookie)
{
return new MyCustomMarshaler();
}
public IntPtr MarshalManagedToNative(Object ManagedObj)
{
MyClassWrapper val = ManagedObj as MyClassWrapper;
return val.ptrToNativeObj;
}
...
}
[return: MarshalAs(UnmanagedType.CustomMarshaler,MarshalType = "MyCustomMarshaler")]
public delegate MyClassWrapper CallbackDotNet();
With this marshaler when C++ calls the C# callback the function MarshalManagedToNative is called on return from that C# callback and this enables to convert the C# reference (to MyClassWrapper) to a pointer to the C++ class MyClass.
This seems sufficient enough and P/Invoke will then take care of dereferencing this MyClass* to a MyClass value.
It wasn't as hard as I thought...