Different function definition after compilation - dll

I have my Keyboard class:
namespace BSGameFramework
{
namespace Input
{
static public ref class Keyboard
{
public:
static KeyboardState GetState();
};
}
}
Where KeyboardState is a ref struct.
After compilation as dll from my C# application I see the function from metadata as follow:
namespace BSGameFramework.Input
{
public class Keyboard
{
public Keyboard();
public static void GetState(ref KeyboardState value);
}
}
Keyboard class has lost its static state and the function GetState is now returning void and taking a KeyboardState as referenced parameter!
Somebody know why? Thanks in advance :D

The reason is because of the return type, ref struct KeyboardState.
In C++/CLI, the "ref" vs. "value" is the thing that determines whether a type is a reference type or a value type, not "class" vs. "struct". ref class and ref struct are both the same thing as C#'s class. value class and value struct are both the same thing as C#'s struct.
Therefore, you have a C++/CLI method declared as returning a reference type, but without the ^. This data type does exist in C++/CLI, but not in C#. The method signature you see is a workaround.
There are two possible solutions to this issue:
Change KeyboardState to a value struct. From what you said, it sounds like you intended for this to be a value type from the beginning, so this is probably the best solution.
Change the return type of the method to KeyboardState^. This will let the method show up in C# the same as it does in C++/CLI. However, if you do this, you'll want to switch all uses of KeyboardState to KeyboardState^. It's a reference type, it should be used with a ^.

Related

Array of a type with different types inside

I'm creating an Array of a Class Type (Button), and I want to have a subclass of it called ButtonMb inside the array Button
Is that possible?
I tried to have two different constructors and use only one Class, but since number of parameters are the same, I couln't reach anywhere.
Here is my code:
for simplicity I only included header code for class declaration
typedef void (*Callback)(void);
typedef int (*CallbackInt)(void);
class Button {
public:
OneButton _pin;
Button(uint8_t pin, Callback click=NULL, Callback longCl=NULL, Callback dblCl=NULL);
void loop();
};
class ButtonMb : public Button {
public:
CallbackInt _pinState;
ButtonMb(CallbackInt pinState, Callback click=NULL, Callback longCl=NULL, Callback dblCl=NULL);
void loop();
};
Button buttons[2] = {
Button(14),
ButtonMb([](){return slaves[0].getState("A15");)
};
Any help?
NOTE: I'm using Arduino, so code can be limited.
Instead of using array of objects, you can create array of pointers. Pointers of base class can point to derived classes, so you can have pointers to objects of different types in one array.

Cannot cast C# enum to C++ enum when enum is in third assembly

I've got a C# class:
public class MyManagedClass
{
public ManagedEnum EnumValue {get; set;}
}
which uses the C# enum
public enum ManagedEnum
{
Enum1,
Enum2
}
This is accessed by a C++/CLI wrapper class and enum:
enum NativeEnum
{
Enum1,
Enum2
};
class WrapperClass
{
public:
WrapperClass(ManagedNamespace::MyManagedClass^ inManaged):
_Managed(inManaged)
{}
NativeEnum GetEnumValue()
{
return (NativeEnum)_Managed->EnumValue;
}
private:
gcroot<ManagedNamespace::MyManagedClass^> _Managed;
};
Now, as long as the C# class and C# enum are in the same assembly, this works fine.
But if the C# enum is in a different C# assembly, the C# class still builds fine, but trying to compile the C++ class yields the error:
error C2440: 'type cast' : cannot convert from 'OtherNamespace::ManagedEnum' to 'OtherNamespace::NativeEnum'
1> Conversion requires a constructor or user-defined-conversion operator, which can't be used by const_cast or reinterpret_cast
Try deriving the underlying value, then cast to the native enum.
This is a crude way of doing it, but may be sufficient in your case.
NativeEnum someMethod(ManagedEnum myEnum)
{
return (NativeEnum)(int)myEnum;
}
Another way is to create a native template method taking both types and the managed enum input, and returning the native type. In such a case, you would have to use reflection to ascertain the underlying type of the managed enum.
In trying out Aaron P's answer, I discovered that the problem was that my C++ project didn't have the C# assembly with the enums in it as a reference. Once I added that reference, it all worked fine.

What are properties in C++/CLI?

I saw in the term property in C++ code. I think it's connected to C++/CLI.
What is it exactly?
It was indeed connected to C++/CLI (unmanaged C++ doesn't really have a notion of properties).
Properties are entities that behave like fields but are internally handled by getter and setter accessor functions. They can be scalar properties (where they behave like a field) or indexed properties (where they behave like an array). In the old syntax, we had to specify the getter and setter methods directly in our code to implement properties - wasn't all that well received as you might guess. In C++/CLI, the syntax is more C#-ish and is easier to write and understand.
Taken from this article: http://www.codeproject.com/KB/mcpp/CppCliProperties.aspx
Also see MSDN on properties in C++/CLI.
Sample code:
private:
String^ lastname;
public:
property String^ LastName
{
String^ get()
{
// return the value of the private field
return lastname;
}
void set(String^ value)
{
// store the value in the private field
lastname = value;
}
}
Yep indeed this is Microsoft's version of managed c++ code or C++/CLI. Now not only do you still have to write Get & Set Methods but you also need to define it as a property. I will say as much as I hate the extra typing the 'Read Only' and 'Write Only' versions of the property is pretty neat.
But unnecessary in un-managed c++!!!
For instance you could write in a class (will do exactly the same thing!):
std::string GetLastName() const { return lastname;}
void SetLastName(std::string lName) { lastname = lName;}
The 'const' made sure it 'GET' was read only, and the set was clear. No need to define a property or add the confusion of String^ vs. std::string....

C++/CLI I can't add a class to my collection

I am attempting to simply add a FilterInfo class to my FilterInfo collection. I'm having a terrible time trying to understand why the following code keeps throwing the error:
System::Collections::Generic::List::Add'
: cannot convert parameter 1 from
'Ziz::FilterInfo *' to
'Ziz::FilterInfo'
I'm only learning C++/CLI, as I'm a C# developer, and I'm sure it's something simple, but I sure could use some pointers. My stripped code is as follows:
public value class FilterInfo
{
public:
char* Address;
};
public ref class Ziz
{
private:
List<FilterInfo>^ _blockList;
public:
// Constructor
Ziz(){
_blockList = gcnew List<FilterInfo>();
}
List<FilterInfo>^ GetBlockList()
{
for each(_IPFILTERINFO ip in _packetFilter->GetBlockList())
{
// _IPFILTERINFO is the native C++ struct.
FilterInfo* f = new FilterInfo();
_blockList->Add(f);
}
return _blockList;
}
You declared _blockList as
List<FilterInfo>^ _blockList;
but you are trying to add
FilterInfo* f
to it. It cannot work since one is a pointer and the other one is a reference.
I'm not sure how "value" fits in but in
public value class FilterInfo
{
public:
char* Address;
};
You are derefore declaring an unmanaged class
to make it managed, you should use
public ref class FiterInfo
This will allow you to use FilterInfo* without having to manage memory explicitely.
Finally, char* is not so great in C++/CLI, I would recommend using System::String
_blockList->Add(*f);
You have to construct your FilterInfo with gcnew as well. You can't really mix and mash these together without marshaling.
FilterInfo is not FilterInfo*. If you want a List of pointers to FilterInfo, you need to say that List<FilterInfo*>. Since FilterInfo is a value class here though you'll likely just want to skip the new.
FilterInfo fi;
_blockList->Add(fi);
public ref class A
{
};
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello World");
ICollection<A^>^ oCollection = gcnew List<A^>();
oCollection->Add(gcnew A());
return 0;
}

C++/CLI: Implementing IList and IList<T> (explicit implementation of a default indexer)

I am trying to implement a C++/CLI class that implements both IList and IList<T>.
Since they have overlapping names, I have to implement one of them explicitly, and the natural choice should be IList.
The implicit implementation of the indexer is:
using namespace System::Collections::Generic;
generic<class InnerT> public ref class MyList : public System::Collections::IList, IList<InnerT> {
// ...
property InnerT default[int]{
virtual InnerT get(int index);
virtual void set(int index, InnerT item);
}
}
I am now trying to declare the default indexer for IList.
My guess would be something like this:
property Object^ System::Collections::IList::default[int]{
virtual Object^ System::Collections::IList::get(int index);
virtual void System::Collections::IList::set(int index, Object^ item);
}
but that just gives me
error C2061: syntax error : identifier 'default'
Any hints?
JaredPar's answer almost worked. Two things should be changed:
The indexer-property needs a different name since "default" is already taken by the implicit implementation.
The specification of the overriding needs to be done on the set- and get-methods, not on the property itself.
I.e.:
property Object^ IListItems[int]{
virtual Object^ get(int index) = System::Collections::IList::default::get;
virtual void set(int index, Object^ item) = System::Collections::IList::default::set;
}
Haven't done a lot of interfaces in C++/CLI but this appears to be covered 8.8.10.1 of the C++/CLI spec. I believe the feature you're looking for is explicit overriding. In this you must specify the implemented member after the definition like so.
property Object^ default[int] = System::Collections::IList::default {... }
I compiled a class implementing IList<T> explicitly written in C# and opened it with Reflector and disassembled to C++/CLI.
T System::Collections::Generic::IList<T>::get_Item(Int32 __gc* index)
{
//
}
void __gc* System::Collections::Generic::IList<T>::set_Item(Int32 __gc* index, T value)
{
//
}
But it doesn't compile: get_Item, set_Item is not a member of IList<T>;