I was given a third party library that wraps unmanaged C++ code into a C# api, one of the functions has a parameter that appears to be a struct from the native global namespace how do we create an instance of that struct in C#?
This is the c++ Struct:
struct GroupInfo
{
int cMembers; // Current # of members in the group.
char saMembers[cMaxMembers][cMaxLoginID + 1]; // Members themselves.
};
When we try to declare an instance of it in C# the compiler says global::GroupInfo is not available due to its protection level.
c++ signature
int QueryGroup(char* sGroupName,
GroupInfo& gi);
C# signature
VMIManaged.QueryGroup(sbyte*, GroupInfo*)
I have a class called group info
class GroupInfo
{
public int cMembers;
public sbyte[,] saMembers;
}
and when i try to implement that using this code i get a cannot convert error
GroupInfo gi = new GroupInfo();
unsafe
{
sbyte* grpName;
fixed (byte* p = groupNameBytes)
{
grpName = (sbyte*)p;
}
return vmi.QueryGroup(grpName, gi); // cannot convert from class GroupInfo to GroupInfo*
}
You are most likely getting the error because of the default protection level of the default constructor in C# for your GroupData class. If it is defined in a different file from the file in which you are trying to use it, defining it like this should work:
class GroupInfo
{
public GroupInfo() {}
public int cMembers;
public sbyte saMembers[cMaxMembers][cMaxLoginID + 1];
};
Related
I have inherited a C++ code base and need to create a new front end. I was hoping to use Windows Universal Runtime Component to combine the UWP to the C++ code. I have had a some success with primitive types and returning them from C++ to the C# code behind. However when i attempt anything non primitive I get this error:
Severity Code Description Project File Line Suppression State
Error C3986 'functionName': signature of public member contains native
type 'C++Class::Struct' (compiling source file
Class.cpp) WRC wrc1\class.h
Is there any way to decorate the C++ classes/structs in a way they would be returnable to the UWP projects C# code behind?
For a struct to be allowed as a public member of a class, you should mark it public value struct.
Here is an example of returning custom struct.
namespace CppComponent
{
// Custom struct
public value struct PlayerData
{
Platform::String^ Name;
int Number;
double ScoringAverage;
};
public ref class Player sealed
{
private:
PlayerData m_player;
public:
property PlayerData PlayerStats
{
PlayerData get(){ return m_player; }
void set(PlayerData data) {m_player = data;}
}
};
}
The documentation has a lot of info, so it might be worth browsing for more info.
I am trying to implement the system above using C++. Previously, I am using C# and OOP to do my programs, so this will be my first time using C++ and I know there is some differences between these two languages.
What I am trying to do is I want to count number of voters in list of members from Logbook class.
In C#, i will use
foreach(Member m in _members) {
if(Member m is Voter) {
votercount++;
}
}
However, i am not sure if in cpp, this implement is correct?
In my Logbook.h file
class Logbook
{
private:
std::list<Member> _members;
In my Logbook.cpp file:
int Logbook::CandidateCount() {
int membercount;
for(Member m: _members) {
if (Member* m=dynamic_cast<const Member*>(&Candidate)) membercount++;
}
return membercount;
}
It display an error at &Candidate where it says identifier Candidate is undefined. Is it because Logbook class can't reach to Candidate class?
Any replies and help is very much appreciated.
There are a few things you are doing wrong here. First you are not initializing your counting variable so it will start off using some random value (it maybe zero or maybe something else).
Next you need to store pointers to the members of your list because in C++ polymorphism only works through pointers. If the list is responsible for deleting its elements (usual) then you should use a smart pointer like std::unique_ptr:
class Logbook {
public:
int CandidateCount();
// virtual destructor is (usually) important for polymorphic types
virtual ~Logbook() = default;
// store pointers in your list
std::list<std::unique_ptr<class Member>> members;
};
Then you can iterate through that list trying to dynamically cast each pointer to the type you want to count. If it returns a valid pointer then you know it is of that type. Otherwise a nullptr will be returned:
class Member: public Logbook {};
class Candidate: public Member {};
class Voter: public Member {};
int Logbook::CandidateCount()
{
int membercount = 0; // initialize this!!!!
for(auto& m : members) { // use reference here to avoid making a copy
if(dynamic_cast<Candidate*>(m.get()))
membercount++;
}
return membercount;
}
Note: If you want to do more than just count your candidates you can keep the pointer obtained from the dynamic cast like this:
class Candidate: public Member { public: void do_something(){} };
int Logbook::CandidateCount()
{
int membercount = 0; // initialize this!!!!
for(auto& m : members) { // use reference here to avoid making a copy
if(auto c = dynamic_cast<Candidate*>(m.get())) {
membercount++;
// c is not nullptr and is type Candidate*
c->do_something(); // use your Candidate like this
}
}
return membercount;
}
int Logbook::CandidateCount()
{
int membercount{};
for(auto const &m : _members) {
if (dynamic_cast<Member*>(m))
++membercount;
}
return membercount;
}
I would try to avoid using RTTI and dynamic_casts in C++, and instead think of a way to implement what you're doing using a different approach, namely by exploiting the OOP nature of the language.
You already have two classes that both inherit from Member, so you could just add a CountMember(int& voterCount) method that lets each member log itself in. You then just call that method for each member you have. Something like this:
class Member {
public:
virtual void countMember(int& voterCount) = 0;
};
class Candidate : public Member {
public:
void countMember(int&) override {}
};
class Voter : public Member {
public:
void countMember(int& voterCount) override {
voterCount++;
}
};
class Logbook {
private:
std::list<Member> _members;
public:
int CandidateCount() {
int votercount = 0;
for(auto& member : _members) {
member.countMember(votercount);
}
return votercount;
}
};
This makes it easy to add custom behaviors in the case new classes were to be added.
I am building a c# application, in my application, I load a c++/cli dll, and calling its function.
I have declare a value class in my c++/cli class.
public value class S_OpenParam {
public :
int iPort;
char* szIpAddress;
int iBaudRate;
};
Then , I am trying to initialize my S_OpenParam in my c# application.
I am facing problem on initialize the char* szIpAddress
myObj.S_OpenParam sParam;
sParam.iBaudRate = 0;
sParam.iPort = 0;
When I try to assign a value to it:
sParam.szIpAddress = "127.0.0.1";
It shows the type is sbyte*
Do you know how to initialize it ?
Since you are in a C++/CLI dll, why don't you use String^ instead of char*, so that you will be able to update it from c# without problems ?
public value class S_OpenParam {
public :
int iPort;
String^ szIpAddress; <-- String^ instead of char*
int iBaudRate;
};
I have a c# application. It references a c++-cli dll. In the namespace of the c++ dll a public struct is declared:
namespace Wrapper {
public struct maxs
{
public:
char Name[255];
int num;
};
etc
There is a function in the c++ dll which returns a pointer to a pointer to the struct.
Wrapper::Maxs** Wrapper::Class1::CallMax();
The C# application references the c++ dll and declares an instance of the struct (unsafe code)
Wrapper.Class1 oMax = new Wrapper.Class1()
Wrapper.Maxs** maxs;
maxs= oMax.CallMax();
int num = oMax.m_num;
Then I try to access the fields of maxs like so:
for(int i = 0; i < num; ++i)
{
name = maxs[i]->name;
}
However, that 'name' field is not visible in the c# intellisense and doesn't compile.
Interestingly I went to see the definition of the struct in the c++ metadata and the struct is blank..
using System;
using System.Runtime.CompilerServices;
namespaceWrapper
{
[CLSCompliant(false)]
[NativeCppClass]
[UnsafeValueType]
public struct Maxs
{
}
}
Is that normal?
So the question is: Why can c# not see the fields of the public struct Maxs? And why is the metadata info show a blank struct?
thanks.
This is the correct answer AFAIK:
Because it is not a managed struct. You'll need to declare it as public value struct instead. – Hans Passant Mar 24 at 2:33
CityHash allows us to generate 128-bit hashes, but the 128-bit representation of an integer is defined as a pair of uint64s (as seen in the header of CityHash.h):
typedef boost::uint64_t uint64;
typedef std::pair<uint64, uint64> uint128;
I have a .NET wrapper that allows me to call the 64-bit version of city hash:
public ref class CityHashDotNet
{
public:
inline static void CityHash64Batch(const char* const value, uint64* const hashes, const int numValues)
{
// I have a wrapper for CityHash that allows me to make batch calls (saves on interops)
CityHashWrapper::CityHash64Batch(value, hashes, numValues);
}
//...
}
This allows me to easily generate hashes from C# by casting the value memory pointer to sbyte* and the hash memory pointer to ulong* and calling the CityHashDotNet wrapper function:
// The MemoryPointer is an IntPtr
CityHashDotNet.CityHash64Batch((sbyte*)values.MemoryPointer, (ulong*)hashes.MemoryPointer, size);
I would like to make a wrapper around the 128-bit version of city hash, but I don't know how to Marshall the std::pair that's necessary for the hash. I defined a class that matches the std::pair and I'm planning on using it to duplicate the std::pair structure:
public class Pair<T1, T2>
{
public T1 First { get; set; }
public T2 Second { get; set; }
public Pair(T1 first, T2 second)
{
First = first;
Second = second;
}
}
The problem is that I have no way to cast an IntPtr to an std::pair<ulong,ulong>* (ulong is the same as uint64). I tried casting it to Pair<ulong,ulong>*, but I get a build error:
// Does not work!
CityHashDotNet.CityHash128Batch((sbyte*)values.MemoryPointer, (Pair<ulong,ulong>*)hashes.MemoryPointer, size);
The error I get is:
error CS1503: ... cannot convert from `Pair<ulong,ulong>*` to `std.pair<unsigned __int64,unsigned __int64>*`
How do I get around this issue?
You can't cast between pointers to these types, they don't have compatible layout. You'll have to copy the data between arrays of each type, one field at a time. Do this inside the C++/CLI code, and let the C# code see nice .NET arrays.