C++/CLI Console::WriteLine with Vector of User Defined Objects - c++-cli

I have a vector of User Defined Objects and I want to simply output some of the class members (fields?) to the console, but when I try, the program crashes. I have tried to display the 'EmployeeID' directly (it's an Int), as well as converting it to a System::String, and a std::string, using several methods for each (marshalling, converting to char array, etc).
std::vector<Employee> employee;
System::Console::WriteLine("Employee ID: " + employee.at(i).getEmployeeId());
I am at a loss as to why I can not simply print to the console, but I'm quite new to CLI, so maybe it's an easy fix and a simple mistake? I have the same issue with other variables stored in the employee object, those other items are std::string type.
I would prefer to use System::String instead of std::string for those other members, but when I do that I get a weird error message ("A member of a non-managed class cannot have a ref class type or interface class type"). Any help with either issue would be great, but this post is specifically for the Console::WriteLine problem.

as per #Ben Voight's suggestions:
Changing my class to a "ref" class and all of my std::string items to System::String^ items has worked.
(It did cause other issues with my code, but that's a different problem and is not related to the the fact that his answer/solution fixed this specific question/problem.)

Related

NSErrorDomain + NS_ERROR_ENUM makes type lookup ambiguous. Why?

I have an error that used to look like this in Objective-C
NSString * const JKConfigurationErrorDomain;
typedef NS_ENUM(NSInteger, JKConfigurationCode) {
JKConfigurationCodeUnknown,
JKConfigurationCodeSomethingBad,
JKConfigurationCodeParsing,
};
Now, this is ugly to use in Swift. But since Swift 4, we can use NSErrorDomain and NS_ERROR_ENUM to make the imported error much nicer in Swift:
NSErrorDomain const JKConfigurationErrorDomain;
typedef NS_ERROR_ENUM(JKConfigurationErrorDomain, JKConfigurationCode) {
JKConfigurationCodeUnknown,
JKConfigurationErrorSomethingBad,
JKConfigurationErrorParsing,
};
This means I can now do stuff in Swift like this:
if let myError = error as? JKConfigurationError, myError.code = .somethingBad {
// handle it
}
instead of having to cast error to NSError, then check its .domain then look at the .code which is an integer, etc.
So far, so good. But my library is called JKConfiguration and there is already a JKConfiguration object (the center piece of the library) in there and as soon as I start using JKConfiguration anywhere in the library code I get an error:
'JKConfiguration' is ambiguous for type lookup in this context
I don't get it, why? What does NSErrorDomain or NS_ERROR_ENUM do such that the type lookup becomes ambiguous and how can I fix it?
What I tried already:
use NS_SWIFT_NAME on the NS_ERROR_ENUM typedef and rename it to something else. Looking at the generated Swift header, the rename works, but doesn't solve the issue
Change the name of the error domain (and thus of the generated error type in Swift). Seems to work according to the generated Swift header, but the issue still persists. Why is that?
The issue is not, as I initially thought, in the name of the error domain. Nor is it a problem with the library name. It’s a problem of the error enum‘s name, in the example above: JKConfigurationCode.
What the Compiler does for the enum cases of an NS_ERROR_ENUM is two-fold:
use the name of the enum and remove that prefix from all enum cases before importing them to swift
create an enum with the given name to hold those cases. If the given name ends with Code remove that suffix.
So that last part is the issue. It means that NS_ERROR_ENUM(AnyDomainName, JKConfigurationCode) generates an enum in Swift to hold the error codes with the name JKConfiguration (without the Code) prefix. But that type already exists in my example, which leads to the ambiguity.
So the solution is to change
NS_ERROR_ENUM(JKConfigurationErrorDomain, JKConfigurationCode)
to
NS_ERROR_ENUM(JKConfigurationErrorDomain, JKConfigurationSomethingCode)
Or similar.
Don’t forget to update all the prefixes of your enum cases though, as it seems the compiler won’t find them if the prefixes don’t match the enum name.
Why doesn’t NS_SWIFT_NAME work to rename the enum?
Best I can tell, NS_SWIFT_NAME causes the type to be renamed but not the cases. This leads to an empty type (Swift chooses a struct in that case) being generated for the error code as Swift doesn’t seem to find the cases. And the original container for the enum cases still has the offending name.

CLI/C++ function overload

I am currently writing a wrapper for a native C++ class in CLI/C++. I am on a little GamePacket class at the moment. Consider the following class:
public ref class GamePacket
{
public:
GamePacket();
~GamePacket();
generic<typename T>
where T : System::ValueType
void Write(T value)
{
this->bw->Write(value);
}
};
I want that I'm able to call the function as following in C#, using my Wrapper:
Packet.Write<Int32>(1234);
Packet.Write<byte>(1);
However, I can't compile my wrapper. Error:
Error 1 error C2664: 'void System::IO::BinaryWriter::Write(System::String ^)' : cannot convert argument 1 from 'T' to 'bool'
I don't understand this error, where does the System::String^ comes from. I'm seeing a lot of overloads of the Write() method, does CLI/C++ not call the correct one, and if so, how can I make it call the correct one?
Reference MSDN: http://msdn.microsoft.com/en-us/library/system.io.binarywriter.write(v=vs.110).aspx
Templates and generics don't work the same.
With templates, the code gets recompiled for each set of parameters, and the results can be pretty different (different local variable types, different function overloads selected). Specialization makes this really powerful.
With generics, the code only gets compiled once, and the overload resolution is done without actually knowing the final parameters. So when you call Write(value), the only things the compiler knows is that
value can be converted to Object^, because everything can
value derives from ValueType, because your constraint tells it
Unfortunately, using just that information, the compiler can't find an overload of Write that can be used.
It seems like you expected it to use Write(bool) when T is bool, Write(int) when T is int, and so on. Templates would work like that. Generics don't.
Your options are:
a dozen different copies of your method, each of which has a fixed argument type that can be used to select the right overload of BinaryWrite::Write
find the overload yourself using reflection, make a delegate matching the right overload, and call it
use expression trees or the dynamic language runtime to find and make a delegate matching the right overload, and then you call it

Data of struct lose after passed from OCX to dll as a parameter of function

In my MFC ActiveX program, there is a calling of function offered by a dll file. And there is a struct type parameter in the function. The strange thing I met is after calling the function, the data in struct is not complete. I am a newer of ActiveX and DLL, and really can't understand how can this happened... The main codes are below:
The defination of struct:
typedef struct{
WORD m_protocol;
WORD m_playstart;
...
char url[128];
char username[MAX_USER_NAME_LEN+1];
char password[MAX_PASSWORD_LEN+1];
}CHANNEL_CLIENTINFO;
The ActiveX codes:
CHANNEL_CLIENTINFO channelInfo;
...
...
GSNET_ClientStart(&channelInfo);
The dll codes:
GSNET_ClientStart(CHANNEL_CLIENTINFO *m_pChaninfo)
{
...
...
}
Can anyone help me? Thanks all.
for more details:
In ActiveX program, before call the GSNET_ClientStart, I initialize the struct with some date. Such as the 'url':
sprintf(channelInfo.url, "192.168.121.122");
And after the calling, in dll function GSNET_ClientStart, I get out the url, it turns out to be "168.121.122", the "192." is missing.
I can make sure that I did't make any mistake in basic grammar.
There is a layout mismatch between the struct definitions in your two modules. It seems that the offset to the url member in your DLL has an offset of 4 more than the offset to that field in your ActiveX.
Make sure that the struct definitions match in both modules. Make sure that the compiler options relating to struct layout are the same in both modules.
I cannot give a definitive fix because there are so many ways in which this mismatch could occur, but for sure the root problem is a mismatch.

How to I pass a checkbox value by reference with CLI?

I have a GUI app written in C++/CLI which has a load of configurable options. I have some overloaded functions which grab values from my data source and I'd like to connect my options to those values.
So here's a couple of data retrievers:
bool GetConfigSingle(long paramToGet, String^% str, char* debug, long debugLength);
bool GetConfigSingle(long paramToGet, bool^% v_value, char* debug, long debugLength);
I was hoping to pass in the checkbox's Checked getter/setter as follows:
result = m_dataSource->GetConfigSingle(CONFIG_OPTION1, this->myOption->Checked, debug, debugLen);
...but for some reason I get an odd compiler error which suggests the Checked value isn't being passed as I'd expect:
1>.\DataInterface.cpp(825) : error C2664: 'bool DataInterface::GetConfigSingle(long,System::String ^%, char*, long)' : cannot convert parameter 2 from 'bool' to 'System::String ^%'
Previously this code passed the checkbox in and modified the values itself, but I'm keen to break the dependency our data collection currently has on windows forms.
So what am I missing here?
[Edit] I've filled out the function definitions as they originally were to avoid confusion - my attempt to reduce the irrelevent information failed.
I'm fairly certain that the CheckBox getter / setter returns a bool.
Figured I'd clarify my comments from above and make it a "real" answer...
When you call Checked, what you're getting back as a return value is a bool that represents the current state of the CheckBox. It is not, however, a reference to the actual data member that holds the CheckBox's state. In fact, a properly encapsulated class shouldn't give access to it. Furthermore, since Checked returns a bool by value, that bool is a temporary object that doesn't necessarily exist by the time GetCongigSingle is called.
This leaves you with several options. Either pass the bools by value, and later set the CheckBox's state, or pass the CheckBox itself by reference and "check" it wherever you want.
The two overload of the method GetConfigSingleFile that you have mentioned both take two arguments whereas you are passing 4 arguments to the method. Are there any default arguments? If yes, can you please reproduce the original method declarations?
Most probably, the 4 argument overload of this method is expecting a String^% as the 2nd argument. This is what the compiler is suggesting anyway. But if we can have a look at the method declarations that could help diagnosing the problem.
This isn't an answer to my question, but worth being aware of - apparently there's a quirk in passing properties by reference.

Declare native types inside a cli class?

I have a
public ref class Test
inside this class, I have:
int frameWidth;
int frameHeight;
int frameStride;
When I try to compile this, I get the error:
error C2664: 'GetImageSize' : cannot convert parameter 1 from 'cli::interior_ptr<Type>' to 'int *'
GetImageSize is a native function and it works only if I move the declaration of the 3 ints above to outside the class or inside the block that calls GetImageSize.
How can I solve this?
Those 3 ints needs to be accessible by more than one function within the class, right now I made it work because I moved them to outside the class, but it's not the right thing to do I believe since they become global.
According to this post, the reason you are seeing this is because the ints are inside a ref class which can be moved around the heap by the garbage collector at will, the address of the ints could change and you wouldn't be told.
To overcome this, you need to tell the GC not to move the objects while you are using them. To do this you need to use
pin_ptr<int*> pinnedFrameWidth = &frameWidth;
then pass pinnedFrameWidth into GetImageSize. The pin_ptr will be automatically cast to int* when passed into the method.
You need to be careful when using pin_ptr. Because the GC can't move the instance of Test class around during a collection, the managed heap can become fragmented and, eventually, performance can suffer. Ideally pin as few objects for the least amount of time possible.
There is a brief discussion of pin pointers in this .Net Rocks show.