Why can't we get a pointer to a generic struct? - 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

Related

Exposing C++ unmanaged code class pointers to C# with C++/CLI

Apologize for my very basic knowledge of C++/CLI, but it has only been introduced to me recently as a necessity. I am trying to create a wrapper for an unmanaged C++ library using C++/CLI. I have followed this helpful guide and can confirm that it (somehow) does the trick. The problem I have is with C++ pointers which expose access to public classes in the unmanaged library. In my CPP library I used them to call the methods from several classes, by having access to a single point-of-entry class. In C# I can't access the methods which should be accessible via these class pointers. I am getting an error stating "the member is inaccessible due to its protection level" and the "->" pointer does not grant access to the exposed class members.
Here's what my code looks like:
UnmanagedCode::ClassA
...
// some code
...
public:
void doSomething() { ... };
ClassB* classB() { return classB.get(); };
private:
std::unique_ptr<ClassB> classB_ {};
C++/CLI wrapper
public ref class WrapperClass: public ManagedObject<UnmanagedCode::ClassA>
{
public:
WrapperClass() : ManagedObject(new UnmanagedCode::ClassA) {}; // template class code in the hyperlink
~WrapperClass() {};
void doSomething() { m_Instance->doSomething(); };
UnmanagedCode::ClassB* classB() { return m_Instance->classB(); };
};
C# program
class Program
{
static void Main(string[] args)
{
WrapperClass w = new WrapperClass();
w.doSomething(); // this works fine
w.classB(); // here is where I am getting the error
}
}
My C# knowledge, is not quite as good as my C++, just as a note.
I dont quite get what you are doing here:
UnmanagedCode::ClassB* classB() { return m_Instance->classB(); };
This line returns a pointer to a ClassB object, that you retrieve in C#. This would require the ClassB to also be a managed object, as far as i know, otherwise, i dont think you can call functions on the object, without creating delegates for unsafe function pointers.
If i understand your issue correct, i think something like this, would fix your issue, in C++:
void* classB() { return static_cast<void*>(m_Instance->classB()); };
void doSomethingWithClassB(void* instance) {static_cast<ClassB*>(instance)->doSomethingWithClassB();}
and then in C#:
class Program
{
static void Main(string[] args)
{
WrapperClass w = new WrapperClass();
w.doSomething();
unsafe
{
void* classb = w.classB();
w.doSomethingWithClassB(classb);
}
}
I think you need to provide a wrapper for each class to make it useful in c#.
I.e. to return the classB you would write something like
MyClassBWrapper^ GetClassB(){ return gcnew MyClassBWrapper(m_Instance->classB());
While you can mess around with pointers in managed code, it requires unsafe code, and is quite cumbersome to use. If you are writing a c++/cli wrapper anyway you should provide a fully managed API. With the possible exception of (optionally) using pointers for large data blocks like images or similar.

Self referencing generic member in struct

I need to broaden my understanding of how structs are compiled when using generics.
I have the following code, which works
public struct TestStruct
{
public GenericStruct<SecondTestStruct> Header;
public int TestValue;
}
public struct GenericStruct<T>
{
public int MessageSize => System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
}
public struct SecondTestStruct
{
public int TestValue;
}
static void Main(string[] args)
{
TestStruct test = new TestStruct();
Console.WriteLine($"Size of test is: {test.Header.MessageSize}");
}
This prints 'Size of test is: 4'
However, if I change TestStruct to attempt to provide its own size:
public struct TestStruct
{
public GenericStruct<TestStruct> Header;
public int TestValue;
}
I get a runtime error: System.TypeLoadException: Could not load type 'TestStructGenerics.TestStruct' from assembly.
I'm guessing it has to do with the compiler being unable to create the struct compile time. Or perhaps a problem with circular references when handling the generic resolution.
EDIT:
I just realized that I can achieve what I want by changing the second case to:
public struct TestStruct
{
public GenericStruct<TestStruct> Header => new GenericStruct<TestStruct>();
public int TestValue;
}
According to Eric Lippert's comments in this Roslyn issue, it is unclear whether this problem is a CLR type loader limitation or whether programs of this sort are invalid, and, if they are invalid, whether the C# compiler should detect it and emit an error. Practically, it appears that, until the authorities decide, we have to avoid type loops between structs regardless of the mode that introduces dependence - instance field (that's never going to work because the struct would be infinite size), static field, implemented generic interface, or generic argument. One can break these struct type loops at compile time by changing some struct in the loop to a class, or at run time by going through object or interface and casting.
See also the corresponding CLR issue.

Calling overloaded C# array access from unmanaged C++/CLI

I have a C# project which has overridden the array access ([]) like so:
Foo.cs:
public override FooItem this[long index] {
...
The project is compiled into a .dll which is referenced by my C++/CLI project.
I have an unmanaged C++/CLI class, FooAccess:
FooAccess.h:
class FooAccess : NativeCPPClass { // not ref class
private:
gcroot<CSDll::Foo^> myFoo;
public:
void Accessor();
In FooAccess.cpp:
void FooAccess::Accessor() {
myFoo->[0]; // doesn't work
myFoo[0]; // doesn't work
pin_ptr<FooItem^> p = &myFoo[0]; // doesn't work
I am stumped.
Note that I am not allowed to edit the C# project and the C++/CLI class cannot be a ref class since it inherits from a native class.
The gcroot template makes the syntax clumsy. You have to cast to convince it to spit out the object reference, ((Foo^)myFoo)[0]. Ugh. The readable way is to lift the object reference out of the template explicitly:
void FooAccess::Accessor() {
Foo^ obj = myFoo;
FooItem^ value = obj[0];
//...
}
But you can write it directly by using the default keyword:
FooItem^ value = myFoo->default[0];
Do consider encapsulation instead of inheritance, storing a private NativeCPPClass* in a ref class, helps you avoid the high cost and awkwardness of gcroot.

Managed struct to/from pointer without boxing?

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.

Does C++ have some analog of C# Type thing to store Types of classes in list / array?

In C# i can create such List to store class types:
List<Type> types_list = new List<Type>();
types_list.add(typeof(int));
types_list.add(typeof(Process)); //and etc
can i do the same in C++?
See typeid and type_info.
Note that you can not use the type_info class to create new instances of the type it represents, as C++ have no support for that.
You can store a type list with boost MPL.
Example:
#include <boost mpl stuff>
int main()
{
typedef boost::mpl::vector<char, int> types;
typedef boost::mpl::push_back<types, Process>::type newTypes;
boost::mpl::at_c<newTypes, 2>::type objectOfTypeProcess;
}
You really shouldn't use this library unless you know what you're doing, so my example isn't that specific. You might have to spend some time to get used to mpl anyway.
It is also possible to create instances dynamically. Inspiring but incomplete code:
class TypeProxy {
public:
virtual ~TypeProxy() = default;
char* create_default() const { return this->create_default_impl(); }
template <typename T>
static scoped_ptr<TypeProxy> CreateProxy ();
private:
virtual void* create_default_impl() const = 0;
};
// A creator class.
template <typename T>
class Concrete : public TypeProxy {
void *create_default_impl() const {
return new T ();
}
};
// Method to create creator proxies.
template <typename T>
scoped_ptr<TypeProxy> TypeProxy::CreateProxy () {
return scoped_ptr<TypeProxy> (new Concrete<T>());
}
Note that this is just some untested scratch code to show the operational mode. Whether to use scoped_ptr is debatable.
You can get more fancy with variadic templates (see e.g. the emplace[_(front|back)] functions in C++11), but it will become complicated as virtual function templates are not allowed, but you somehow have to pass over argument lists nevertheless.
(sidenote: boost::shared_ptr use a similar virtual/template mix, which is why you can use non-polymorphic base classes and custom deleters with it)

Categories

Resources