System::String/StringBuilder as out parameter
-
Hello All, What is the recommended way to handle strings out parameters of functions/methods? Interestingly, I could not find single example in any book and just not confidence on which way will work and across languages like VB.NET and C#. Any help? The simply one I am currently considering (which works) is:
#using using namespace System;
using namespace System::Text;void GetString(StringBuilder* pOut)
{
pOut->set_Length(0);
pOut->Append("Out--string");
}int _tmain()
{
StringBuilder* pOut = new StringBuilder("In--string");GetString(pOut);
Console::WriteLine(pOut->ToString());
return 0;
}Any better solution? Something that could be used in other languages too. Best regards, Paul. Jesus Christ is LOVE! Please tell somebody.
-
Hello All, What is the recommended way to handle strings out parameters of functions/methods? Interestingly, I could not find single example in any book and just not confidence on which way will work and across languages like VB.NET and C#. Any help? The simply one I am currently considering (which works) is:
#using using namespace System;
using namespace System::Text;void GetString(StringBuilder* pOut)
{
pOut->set_Length(0);
pOut->Append("Out--string");
}int _tmain()
{
StringBuilder* pOut = new StringBuilder("In--string");GetString(pOut);
Console::WriteLine(pOut->ToString());
return 0;
}Any better solution? Something that could be used in other languages too. Best regards, Paul. Jesus Christ is LOVE! Please tell somebody.
If all you are looking to do is get a String/StringBuilder back when passed as a parameter, then passing by reference will achieve what you want. Your current example would need to be modified to something like:
void GetString(StringBuilder **pOut) { (*pOut)->set_Length(0); (*pOut)->Append(S"Out--string"); }
This is very COM-like, and both C# and VB.Net prototypes will ask for a reference, such as:void GetString(ref StringBuilder pOut); //in C#
which would be called like:GetString(ref pOut); //C# GetString(ByRef pOut); //VB.Net GetString(&pOut); //MC++
For various reasons, just asking for a pointer to a parameter is not enough with the CLR (unlike in C++); one must ask for a pointer to a pointer in order for the modification to get back to the caller. Your current version may well work when the StringBuilder is not reallocated, but all bets are off after reallocation, since the callee will then have a pointer to a new object (and this is always the case when passing String). I believe there is also a way to make the call look like:void GetString(out StringBuilder pOut); //C#
but I have not bothered so far, as I am waiting for finalisation of VS 2003 with possibly further MC++ refinements, and I have also not really needed to do so. When passing by the CLR's definition of "by ref", you will no longer need to pre-size the StringBuilder before calling GetString():int _tmain() { StringBuilder *pOut = new StringBuilder(); GetString(&pOut); Console::WriteLine(pOut->ToString()); return 0; }
That is probably obvious to you, but it is mainly why I have used passing by ref. For example, I have written some MC++ functions that are expected to be called repeatedly. They take StringBuilders by ref specifically so that internal arrays rarely need to be reallocated, and I suspect you might be trying to do the same. I have also done this with plain CLR strings, though reallocation would need to occur on each call:void GetString(String **ppStr) { *ppStr = new String(S"Out--string"); }
However, if the function has no length of string to return, then chars do not have to be allocated for nothing. I hope some of this is valid for your purposes :) Cheers -
If all you are looking to do is get a String/StringBuilder back when passed as a parameter, then passing by reference will achieve what you want. Your current example would need to be modified to something like:
void GetString(StringBuilder **pOut) { (*pOut)->set_Length(0); (*pOut)->Append(S"Out--string"); }
This is very COM-like, and both C# and VB.Net prototypes will ask for a reference, such as:void GetString(ref StringBuilder pOut); //in C#
which would be called like:GetString(ref pOut); //C# GetString(ByRef pOut); //VB.Net GetString(&pOut); //MC++
For various reasons, just asking for a pointer to a parameter is not enough with the CLR (unlike in C++); one must ask for a pointer to a pointer in order for the modification to get back to the caller. Your current version may well work when the StringBuilder is not reallocated, but all bets are off after reallocation, since the callee will then have a pointer to a new object (and this is always the case when passing String). I believe there is also a way to make the call look like:void GetString(out StringBuilder pOut); //C#
but I have not bothered so far, as I am waiting for finalisation of VS 2003 with possibly further MC++ refinements, and I have also not really needed to do so. When passing by the CLR's definition of "by ref", you will no longer need to pre-size the StringBuilder before calling GetString():int _tmain() { StringBuilder *pOut = new StringBuilder(); GetString(&pOut); Console::WriteLine(pOut->ToString()); return 0; }
That is probably obvious to you, but it is mainly why I have used passing by ref. For example, I have written some MC++ functions that are expected to be called repeatedly. They take StringBuilders by ref specifically so that internal arrays rarely need to be reallocated, and I suspect you might be trying to do the same. I have also done this with plain CLR strings, though reallocation would need to occur on each call:void GetString(String **ppStr) { *ppStr = new String(S"Out--string"); }
However, if the function has no length of string to return, then chars do not have to be allocated for nothing. I hope some of this is valid for your purposes :) CheersHello Jeff, Thanks so much for taken the time to explain this so well. I tried the pointer to pointer stuff in an out System::Drawing::Rectangle case, and the compile will not work claiming it is a value and that I should use the "__nogc new" instead:
void GetRectangle(Rectangle** pOut)
{
*pOut = new Rectangle(...);
}Not knowing whether the C++ heap will be accessible or not from other languages, I changed it to reference like this:
void GetRectangle(Rectangle& pOut)
{
pOut.X = ;...
}However, the IL output from the MC++ seems to carry some VC specific information attached to the parameter:
modopt([Microsoft.VisualC]Microsoft.VisualC.IsCXXReferenceModifier)
So, I got confused as to what will really be accepted as the standard way of doing it. Best regards, Paul. Jesus Christ is LOVE! Please tell somebody.
-
Hello Jeff, Thanks so much for taken the time to explain this so well. I tried the pointer to pointer stuff in an out System::Drawing::Rectangle case, and the compile will not work claiming it is a value and that I should use the "__nogc new" instead:
void GetRectangle(Rectangle** pOut)
{
*pOut = new Rectangle(...);
}Not knowing whether the C++ heap will be accessible or not from other languages, I changed it to reference like this:
void GetRectangle(Rectangle& pOut)
{
pOut.X = ;...
}However, the IL output from the MC++ seems to carry some VC specific information attached to the parameter:
modopt([Microsoft.VisualC]Microsoft.VisualC.IsCXXReferenceModifier)
So, I got confused as to what will really be accepted as the standard way of doing it. Best regards, Paul. Jesus Christ is LOVE! Please tell somebody.
Well, I just got this response on the VC news group:
There is no real need to change to pointers. C# handles this fine. The
"modopt" means that it is an OPTional MODifier for the signature. When the
C++ compiler sees it it know to treat it as a C++ reference, the C# compiler
will just treat it as a byref parameter.Ronald Laeremans
Visual C++ teamIt seems the modopt stuff is "safe" to work with :) Best regards, Paul. Jesus Christ is LOVE! Please tell somebody.
-
Well, I just got this response on the VC news group:
There is no real need to change to pointers. C# handles this fine. The
"modopt" means that it is an OPTional MODifier for the signature. When the
C++ compiler sees it it know to treat it as a C++ reference, the C# compiler
will just treat it as a byref parameter.Ronald Laeremans
Visual C++ teamIt seems the modopt stuff is "safe" to work with :) Best regards, Paul. Jesus Christ is LOVE! Please tell somebody.
Hi Paul, Thanks for the bit from the Ronald Laeremans; there's nothing like inside info :) I had been wondering why the compiler treats pointers to __value parameters the same as declaring them as C++ references. Knowing that this stuff is ".Net official" makes me feel better about the future for code I've written. Please feel free to post anything else you get from the inside! Sorry I didn't cover passing __value types in my first post; I was only talking about types that are already "CLR reference object"/pointer types, when I talked about pointers to pointers. I pass __value types the same way you did (as a C++ reference), and skip the pointer syntax. Cheers, Jeff