How to get a String^ into a 2d char array? - visual-c++

I'm trying to copy characters from a System::String ^ to a rectangular char array.
First I tried: (along with some other code. not relevant to the question)
char name[25][21];
...
void savedata(int x, System::String ^ a){ //x is the student #, a is the name
int b;
using namespace System::Runtime::InteropServices; // for class Marshal
char* buffer((char*)(void*)Marshal::StringToHGlobalAnsi(a));
x--; //So we write buffer[b] at data[0][b] when int x is 1
for(b = 0; b < 21; b++){
data[x][b] = buffer[b];
};
}
and when I tried to run and debug it, "An unhandled exception of type 'System.AccessViolationException'" occurred
Is there some easier/better way to put a String^ into a (2 dimensional) char array, and if not, what am I doing wrong here?

You should be calling .ToPointer() to convert the result of StringToHGlobalAnsi to something that you can then cast to char*.
You should also call FreeHGlobal on the result of StringToHGlobalAnsi (or you can recreate an IntPtr from your char*).

Related

Warning : comparison between pointer and integer

struct smt{
char *c;
};
int main(){
char *w="astring";
if(smt->c == w[0])
...do something
}
How do I fix the warning that I get in the if and what exacly causes it?
The warning shows up because you're comparing smt->c, which is char*, to w[0], which is a character (that for this comparison gets implicitly casted to int).
You probably meant comparing the first character like this:
if(smt->c[0] == w[0]) { ... }
If you want to compare full strings, use
if(strcmp(smt->c, w) == 0) { ... }
or even better, use strncmp if you know the maximum length the strings can have.
The error comes from the fact that often (almost always), you don't want to compare an adress (pointer) with a character.
You're comparing a char* c with a char 'a'. What you want to do is this I believe:
struct smt{
char *c;
};
int main(){
char *w="astring";
// Here smt->c returns a char*
// w[0] gets you the first character, so 'a'
if(strcmp(smt->c, w) == 0)
...do something
}
If you want to compare the first characters of both strings, you have to add [0] to smt->c

How can I understand this code (string as a function parameter in c)

I have written this code but its not working but when I replace *targ and *sour by targ[] and sour[] then its working. Also it shows many error when I call the function converge like this converge(*targ, *sour). Please someone help me to understand this.
#include<stdio.h>
#include<string.h>
void converge(char *target, char *src);
int main()
{
char *targ = "xxxxxxxxxxxxxxxxxxx";
char *sour = "yyyyyyyyyyyyyyyyyyy";
converge(targ, sour);
//printf("%s", targ);
}
void converge(char *target, char *src)
{
int i, j;
for(i=0,j=strlen(src); i<=j; i++, j--)
{
target[i]= src[i];
target[j]= src[j];
printf("%s\n",target);
}
}
If you define a string like this:
char *targ = "abcd";
it is treated as read-only value, since the string "abcd" is stored in read-only memory, while the pointer targ is stored on your stack. Depending on your compiler you might get some warning unless you make this more explicit with const char *targ = "abcd";. An assignment like targ[i] = src[i]; is not allowed in this case.
If you define a string like this:
char targ[] = "abcd";
a char-array will be created on your data stack, this string can be changed, since data on your stack is readable and writable. Additionally you can access the first element of your array as pointer.

C++/CLI from tracking reference to (native) reference - wrapping

I need a C# interface to call some native C++ code via the CLI dialect. The C# interface uses the out attribute specifier in front of the required parameters. That translates to a % tracking reference in C++/CLI.
The method I has the following signature and body (it is calling another native method to do the job):
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
mRenderWindow->getMetrics(width, height, colourDepth, left, top);
}
Now the code won't compile because of a few compile time errors (all being related to not being able to convert parameter 1 from 'unsigned int' to 'unsigned int &').
As a modest C++ programmer, to me CLI is looking like Dutch to a German speaker. What can be done to make this wrapper work properly in CLI?
Like it was also suggested in a deleted answer, I did the obvious and used local variables to pass the relevant values around:
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
unsigned int w = width, h = height, c = colourDepth;
int l = left, t = top;
mRenderWindow->getMetrics(w, h, c, l, t);
width = w; height = h; colourDepth = c; left = l; top = t;
}
It was a bit obvious since the rather intuitive mechanism of tracked references: they're affected by the garbage collector's work and are not really that static/constant as normal &references when they're prone to be put somewhere else in memory. Thus this is the only way reliable enough to overcome the issue. Thanks to the initial answer.
If your parameters use 'out' on the C# side, you need to define your C++/CLI parameters like this: [Out] unsigned int ^%width
Here's an example:
virtual void __clrcall GetMetrics([Out] unsigned int ^%width)
{
width = gcnew UInt32(42);
}
Then on your C# side, you'll get back 42:
ValueType vt;
var res = cppClass.GetMetrics(out vt);
//vt == 42
In order to use the [Out] parameter on the C++/CLI side you'll need to include:
using namespace System::Runtime::InteropServices;
Hope this helps!
You can use pin_ptr so that 'width' doesn't move when native code changes it. The managed side suffers from pin_ptr, but I don't think you can get around that if you want native code directly access it without 'w'.
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
pin_ptr<unsigned int> pw = &width; //do the same for height
mRenderWindow->getMetrics(*pw, h, c, l, t);
}

C++/CLI String Conversions

I found this really nice piece of code that converts a string to a System:String^ as in:
System::String^ rtn = gcnew String(move.c_str()); // 'move' here is the string
I'm passing rtn back to a C# program. Anyways, inside the function where this code exists, I'm passing in a System::String^. I also found some code to convert a System:String^ to a string using the following code:
pin_ptr<const wchar_t> wch = PtrToStringChars(cmd); // 'cmd' here is the System:String
size_t convertedChars = 0;
size_t sizeInBytes = ((cmd->Length + 1) * 2);
errno_t err = 0;
char *ch = (char *)malloc(sizeInBytes);
err = wcstombs_s(&convertedChars,ch, sizeInBytes,wch, sizeInBytes);
Now I can use 'ch' as a string.
This, however, seems to be alot more work than converting the other way using the gcnew. So, at last my question is, is there something out there that will convert a System::String^ to string using a similar fashion as with the gcnew way?
Use VC++'s marshaling library: Overview of Marshaling in C++
#include <msclr/marshal_cppstd.h>
// given System::String^ mstr
std::string nstr = msclr::interop::marshal_as<std::string>(mstr);
this could be useful:
wchar_t *str = "Hi StackOverflow"; //native
String^ mstr= Marshal::PtrToStringAnsi((IntPtr)str); // native to safe managed
wchar_t* A=( wchar_t* )Marshal::StringToHGlobalAnsi(mstr).ToPointer(); // return back to native
don't forget using namespace System::Runtime::InteropServices;

Having trouble passing array to function

I am getting all kinds of errors when passing my array to this function. The function is suppose to have the user enter a name and a score and store them in 2 seperate arrays, one for the names, one for the scores. I believe I have to use pointers but have no idea on how to use them. I don't want the answer, just a push in the right direction. Here is the code:
#include <iostream>
int InputData(int &, char, int);
using namespace std;
int main()
{
char playerName[100][20];
int score[100];
int numPlayers = 0;
InputData(numPlayers, playerName, score);
return 0;
}
int InputData(int &numPlayers, char playerName[][20], int score[])
{
while (numPlayers <= 100)
{
cout << "Enter Player Name (Q to quit): ";
cin.getline(playerName, 100, ā€˜\nā€™);
if ((playerName[numPlayers] = 'Q') || (playerName[numPlayers] = 'q'))
return 0;
cout << "Enter score for " << playerName[numPlayers] <<": ";
cin >> score[numPlayers];
numPlayers++;
}
}
Ok, I made some more changes and the errors are less, must be getting close, Lol!
This looks like a school assignment and I applaud you for not asking for the answer. There are several ways to do it, but you are already fairly close in the approach that you are using. When you pass an array reference, you do not want to include the length of the array. For example, the parameter int score[100] should be int score[]. The exception, especially in your scenario, is with multidimensional arrays. In this case, you want to use char playerName[][20]. Your function declaration also needs to change to match. Don't forget InputData returns an int. Your declarations and function call are correct; you just need to adjust your function signature.
Keeping the errors aside -
InputData(numPlayers, playerName, score, size);
// ^^^^ size is no where declared
// resulting Undeclared indentifier error
Prototype mentions of taking 3 arguments but calling the function passing 4 parameters.
Hint regarding errors:
An 1D array decays to a pointer pointing to first element in the array while passing to a function.
A 2D array decays to a pointer pointing to the 1D array ( i.e., T[][size] ) while passing to a function.
Return type of main() should be int.
It seems with the given hints you corrected most of the errors. But you forgot to change the prototype. So, change -
int InputData(int &, char, int);
to
int InputData(int &, char[][20], int[]);
Why aren't you using std::string array for player names ? Use it and remove rest of the errors. Good luck.

Resources