Click here to Skip to main content
12,635,441 members (25,516 online)
Click here to Skip to main content
Add your own
alternative version

Stats

143.3K views
1.3K downloads
50 bookmarked
Posted

StringConvertor : A convertor class for managed-unmanaged string conversions that handles memory de-allocations

, 15 May 2005 CPOL
Rate this:
Please Sign up or sign in to vote.
A convertor class for managed-unmanaged string conversions that handles memory de-allocations. Caller need not worry about freeing unmanaged memory allocations.

Contents

Overview

This article presents a C++/CLI class <CODE lang=mc++>StringConvertor which can be used to make conversions between <CODE lang=mc++>System::String and native types like a <CODE lang=mc++>char*, <CODE lang=mc++>BSTR etc. The class keeps track of allocated unmanaged memory handles and frees them automatically, so the caller needn't worry about freeing up unmanaged resources.

The class has several constructor overloads that accept various native types (as well as managed types) like <CODE lang=mc++>char*, <CODE lang=mc++>__wchar_t* etc and internally maintains a <CODE lang=mc++>System::String object that can be extracted using the <CODE lang=mc++>String operator or the <CODE lang=mc++>ToString override. There are also various properties each exposing a native type like <CODE lang=mc++>char*, <CODE lang=mc++>BSTR etc which can be used to convert the string to your preferred unmanaged type.

The class reference follows with suitable examples. Make sure you read the Class usage tips section at the end though.

[Back to Contents]

Class Reference : <CODE lang=mc++>StringUtilities::StringConvertor

Namespace

The <CODE lang=mc++>StringConvertor class is declared in the <CODE lang=mc++>StringUtilities namespace.

Constructors

<CODE lang=mc++>StringConvertor(String^ s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified <CODE lang=mc++>System::String object. Throws an exception if a <CODE lang=mc++>nullptr is passed to it.<PRE lang=mc++>String^ str1 = "Hello world"; StringConvertor sc1(str1);

<CODE lang=mc++>StringConvertor(const char* s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified <CODE lang=mc++>const char*. Throws an exception if a <CODE lang=mc++>nullptr (equates to <CODE lang=mc++>null for native objects)<CODE lang=mc++> is passed to it.<PRE lang=mc++>const char* str2 = "Apples and oranges"; StringConvertor sc2(str2);

<CODE lang=mc++>StringConvertor(const __wchar_t* s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified <CODE lang=mc++>const __wchar_t*. Throws an exception if a <CODE lang=mc++>nullptr (equates to <CODE lang=mc++>null for native objects)<CODE lang=mc++> is passed to it.<PRE lang=mc++>const __wchar_t* str3 = L"Apples and oranges"; StringConvertor sc3(str3);

<CODE lang=mc++>StringConvertor(array<Char>^ s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified CLI array of type <CODE lang=mc++>Char. Throws an exception if a <CODE lang=mc++>nullptr is passed to it.<PRE lang=mc++>array<Char>^ str4 = {'H', 'e', 'y', '.'}; StringConvertor sc4(str4);

<CODE lang=mc++>StringConvertor(BSTR s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified <CODE lang=mc++>BSTR. Throws an exception if a <CODE lang=mc++>nullptr (equates to <CODE lang=mc++>null for native objects)<CODE lang=mc++> is passed to it.<PRE lang=mc++>BSTR str5 = SysAllocString(L"Interesting"); StringConvertor sc5(str5); SysFreeString(str5);

<CODE lang=mc++>StringConvertor(std::string s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified <CODE lang=mc++>std::string. No exception(s) thrown.<PRE lang=mc++>std::string str6 = "STL is kinda handy"; StringConvertor sc6(str6);

<CODE lang=mc++>StringConvertor(std::wstring s)

Initializes a new instance of the <CODE lang=mc++>StringConvertor class using the specified <CODE lang=mc++>std::wstring. No exception(s) thrown.<PRE lang=mc++>std::wstring str7 = L"STL is kinda handy"; StringConvertor sc7(str7);

Methods

<CODE lang=mc++>virtual String^ ToString() override

Override that converts the <CODE lang=mc++>StringConvertor to a <CODE lang=mc++>System::String (returns the underlying <CODE lang=mc++>String object).<PRE lang=mc++>StringConvertor sc1(str1); int len = sc1.ToString()->Length; Console::WriteLine(len);

Operators

<CODE lang=mc++>operator String^()

Operator overload that converts the <CODE lang=mc++>StringConvertor to a <CODE lang=mc++>System::String (returns the underlying <CODE lang=mc++>String object).<PRE lang=mc++>StringConvertor sc1(str1); Console::WriteLine(sc1); //Operator String^ invoked

Properties

All properties in the class are read-only.

<CODE lang=mc++>interior_ptr<const Char> InteriorConstCharPtr

Returns an interior pointer to the underlying string. The pointer is of type <CODE lang=mc++>const Char so you can only read through it.<PRE lang=mc++>interior_ptr<const Char> p1 = sc1.InteriorConstCharPtr; for(interior_ptr<const Char> tmp=p1; *tmp; tmp++) Console::Write(*tmp);

<CODE lang=mc++>interior_ptr<Char> InteriorCharPtr

Returns an interior pointer to the underlying string that can be written to. Use with extreme care, as you are directly editing the <CODE lang=mc++>System::String's internal character buffer.<PRE lang=mc++>interior_ptr<Char> p2 = sc2.InteriorCharPtr; for(interior_ptr<Char> tmp=p2; *tmp; tmp++) if(Char::IsLower(*tmp)) //swap case *tmp = Char::ToUpper(*tmp); else *tmp = Char::ToLower(*tmp); Console::WriteLine(sc2);

<CODE lang=mc++>char* NativeCharPtr

Returns a <CODE lang=mc++>char* (allocated on the native heap). The class handles cleaning up of this memory.<PRE lang=mc++>char* p3 = sc1.NativeCharPtr; printf("%s \r\n", p3);

<CODE lang=mc++>__wchar_t* NativeWideCharPtr

Returns a <CODE lang=mc++>__wchar_t* (allocated on the native heap). The class handles cleaning up of this memory.<PRE lang=mc++>__wchar_t* p4 = sc1.NativeWideCharPtr; printf("%S \r\n", p4);

Note - I've included a generic text mapping, <CODE lang=mc++>NativeTCharPtr, for these two properties so you can use it with an <CODE lang=mc++>LPTSTR. Since it's a <CODE lang=mc++>#define, intellisense will not detect it. <CODE lang=mc++>NativeTCharPtr maps to <CODE lang=mc++>NativeWideCharPtr if <CODE lang=mc++>_UNICODE is defined, else to <CODE lang=mc++>NativeCharPtr.

<PRE lang=mc++>LPTSTR p5 = sc1.NativeTCharPtr; _tprintf(_T("%s \r\n"),p5);

<CODE lang=mc++>array<Char>^ CharArray

Returns a CLI array of Chars.<PRE lang=mc++>array<Char>^ arr1 = sc5.CharArray; for each(Char c in arr1) Console::Write(c);

<CODE lang=mc++>BSTR BSTRCopy

Returns a <CODE lang=mc++>BSTR allocated natively (unmanaged heap). The class handles cleaning up of this memory.<PRE lang=mc++>BSTR b1 = sc5.BSTRCopy; printf("BSTR contains %S \r\n", b1);

Each call creates a new <CODE lang=mc++>BSTR, and if you change the contents of the returned <CODE lang=mc++>BSTRs, newly returned <CODE lang=mc++>BSTRs will contain the original string; for example, in the below snippet, <CODE lang=mc++>b1 and <CODE lang=mc++>b2 are not the same string any longer.

<PRE lang=mc++>BSTR b1 = sc5.BSTRCopy; b1[0] = L'X'; BSTR b2 = sc5.BSTRCopy;

If for any reason, you want to change the string represented by the <CODE lang=mc++>StringConvertor, use the <CODE lang=mc++>InteriorCharPtr property.

<CODE lang=mc++>std::string STLAnsiString

Returns an <CODE lang=mc++>std::string object.<PRE lang=mc++>std::string s1 = sc5.STLAnsiString; printf("%s \r\n", s1.c_str());

<CODE lang=mc++>std::wstring STLWideString

Returns an <CODE lang=mc++>std::wstring object.<PRE lang=mc++>std::wstring s2 = sc5.STLWideString; printf("%S \r\n", s2.c_str());

[Back to Contents]

Class usage tips

Two of the primary purposes of the class are (1) to make it easy to convert from <CODE lang=mc++>System::String to unmanaged types and vice-versa and (2) to free the user from the responsibility of freeing up the unmanaged memory allocations. The various properties and constructors handle the first role and the destructor handles the second one. When I say destructor, I mean destructor (not the finalizer), which means the destructor needs to get called as soon as the object goes out of scope or has served its purpose. For that, it's recommended that you either use the auto-variable non-handle declaration format or explicitly call <CODE lang=mc++>delete on the <CODE lang=mc++>StringConvertor object after use.

<PRE lang=mc++>void SomeFunc(...) { StringConvertor s1(...); //... } //Destructor gets called

or

<PRE lang=mc++>StringConvertor^ s1 = gcnew StringConvertor(...); //... delete s1; //Destructor gets called

Try to avoid making a <CODE lang=mc++>StringConvertor a member of a long-living class (or better still, never make it a class-member). Always try and limit the scope and life-time of <CODE lang=mc++>StringConvertor objects. While, a finalizer has been provided (for unanticipated circumstances), ideally, the finalizer should never need to be called. To help you with avoiding this, in debug builds, an exception gets thrown if the finalizer is ever called and in release builds, <CODE lang=mc++>Trace::WriteLine is used to write a warning message to any active trace listeners.

When you have functions returning unmanaged strings (like a <CODE lang=mc++>char*), you need to make sure that the pointer you return is allocated by you separately (since the pointer returned by <CODE lang=mc++>StringConvertor will be freed when the <CODE lang=mc++>StringConvertor goes out of scope. The snippets below show two functions, one returning a <CODE lang=mc++>BSTR and the other returning a <CODE lang=mc++>char*.

<PRE lang=mc++>BSTR ReturnBSTR(String^ s) { return SysAllocString(StringConvertor(s).BSTRCopy); }<PRE lang=mc++>char* ReturnCharPointer(String^ s) { StringConvertor sc(s); char* str = new char[sc.ToString()->Length + 1]; strcpy(str, sc.NativeCharPtr); return str; }

And when you've done using the strings returned by these functions, you need to free the pointers manually.

<PRE lang=mc++>BSTR s = ReturnBSTR(gcnew String("Hello there")); printf("%S \r\n",s); SysFreeString(s);<PRE lang=mc++>char* str = ReturnCharPointer(gcnew String("Hello again")); printf("%s \r\n",str); delete[] str;

[Back to Contents]

Design notes

Most of the data conversions make use of the <CODE lang=mc++>Marshal class from the <CODE lang=mc++>System::Runtime::InteropServices namespace. Now most of these functions allocate memory in the native heap and the class uses two <CODE lang=mc++>vector<> objects to keep track of all allocated memory handles. One <CODE lang=mc++>vector<> stores all the pointers that need to be freed using <CODE lang=mc++>Marshal::FreeHGlobal while the other one stores all the <CODE lang=mc++>BSTR pointers that need to be freed using <CODE lang=mc++>Marshal::FreeBSTR. The initialization and clean-up of these <CODE lang=mc++>vectors is done by the <CODE lang=mc++>StringConvertorBase class which is an <CODE lang=mc++>abstract <CODE lang=mc++>ref class that's the base class for the <CODE lang=mc++>StringConvertor class.

Initially, I had designed the class so that the unmanaged pointers are cached, so that multiple calls to any of the properties that returned pointers to the native heap always returned the cached pointer. But I soon saw that this meant that, if the caller modifies data using one of these pointers, it dirties the internal state of the <CODE lang=mc++>StringConvertor object which was obviously something that should not be allowed. Considering that, managed-unmanaged transitions should not be over-used and that in most frequent situations, the required life-time of the unmanaged data is very small, usually for making a function call, I assumed that any user of this class with any level of adequacy as a programmer would never abuse the class to such an extent that there'd be lots of unmanaged memory lying un-freed. So the <CODE lang=mc++>vectors and the unmanaged pointers/handles they contain are freed up in the class destructor (as well as in the finalizer as a safety measure).

As earlier mentioned, if for any reason, you want to change the internal string of the <CODE lang=mc++>StringConvertor object, use the <CODE lang=mc++>InteriorCharPtr property and write to the buffer directly - but be very careful how you use it.

Why I left <CODE lang=mc++>CString out?

Conversions to <CODE lang=mc++>CString and from <CODE lang=mc++>CString are so trivial, that it didn't make sense to add them to this class.

<PRE lang=mc++>CString str("Hello"); String^ s = gcnew String(str); //CString to String CString strnew(s); //String to CString

[Back to Contents]

Conclusion

I believe I've covered all the basic data types and that these types can be used to create any other more complex data types. For example, the <CODE lang=mc++>BSTR can be used to make a <CODE lang=mc++>CComBSTR or a <CODE lang=mc++>_bstr_t, while the <CODE lang=mc++>char* (or <CODE lang=mc++>__wchar_t*) can be used to make a <CODE lang=mc++>CString.

Suggestions and feedback are welcome.

[Back to Contents]

History

  • May 15, 2005 : Class completed and article first published on The Code Project

[Back to Contents]

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nish Nishant
United States United States
Nish Nishant is a Software Architect/Consultant based out of Columbus, Ohio. He has over 16 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish is a recipient of the annual Microsoft Visual C++ MVP Award since 2002 (14 consecutive awards as of 2015).

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored
C++/CLI in Action for Manning Publications in 2005, and had previously co-authored
Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his
WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : You can reach Nish on his google email id voidnish.

Website and Blog

You may also be interested in...

Comments and Discussions

 
Questionhey Pin
Member 851248417-Feb-12 2:59
memberMember 851248417-Feb-12 2:59 
Thank you Smile | :)
QuestionCompiler troubles Pin
David Fenstemaker31-Jan-12 15:31
memberDavid Fenstemaker31-Jan-12 15:31 
AnswerRe: Compiler troubles Pin
Nishant Sivakumar1-Feb-12 3:12
mvpNishant Sivakumar1-Feb-12 3:12 
GeneralRe: Compiler troubles Pin
David Fenstemaker2-Feb-12 12:04
memberDavid Fenstemaker2-Feb-12 12:04 
GeneralMy vote of 5 Pin
Mark A Manning4-Oct-11 10:25
memberMark A Manning4-Oct-11 10:25 
GeneralCompiler error C2039 Pin
butch8016-Oct-07 17:18
memberbutch8016-Oct-07 17:18 
GeneralI wish I'd seen this before Pin
Asheem mamoowala19-Sep-07 15:09
memberAsheem mamoowala19-Sep-07 15:09 
General[Message Removed] Pin
nompel5-Oct-08 1:03
membernompel5-Oct-08 1:03 
GeneralRe: I wish I'd seen this before Pin
varnk26-Aug-11 8:48
membervarnk26-Aug-11 8:48 
QuestionBunch of compiler errors. Pin
foahchon25-Aug-06 22:56
memberfoahchon25-Aug-06 22:56 
AnswerRe: Bunch of compiler errors. Pin
Nishant Sivakumar26-Aug-06 3:49
staffNishant Sivakumar26-Aug-06 3:49 
QuestionRe: Bunch of compiler errors. Pin
foahchon26-Aug-06 9:09
memberfoahchon26-Aug-06 9:09 
AnswerRe: Bunch of compiler errors. Pin
Nishant Sivakumar26-Aug-06 9:26
staffNishant Sivakumar26-Aug-06 9:26 
GeneralReally Nice Class Pin
luis.filipe.sousa16-Jun-06 2:30
memberluis.filipe.sousa16-Jun-06 2:30 
GeneralGreat class! Pin
jrocket17-May-06 4:22
memberjrocket17-May-06 4:22 
QuestionUsage from another assembly? Pin
Doc Lobster24-Apr-06 3:28
memberDoc Lobster24-Apr-06 3:28 
AnswerRe: Usage from another assembly? Pin
Aki99931-Jul-07 6:24
memberAki99931-Jul-07 6:24 
Questionhow to convert std::string to CString,Th U Pin
Yang Jing6-Apr-06 23:50
memberYang Jing6-Apr-06 23:50 
GeneralGreat class! I use it quite a bit! Pin
Tom Archer - MSFT25-Feb-06 14:02
memberTom Archer - MSFT25-Feb-06 14:02 
GeneralRe: Great class! I use it quite a bit! Pin
Nishant Sivakumar26-Feb-06 10:25
staffNishant Sivakumar26-Feb-06 10:25 
GeneralRe: Great class! I use it quite a bit! Pin
Tom Archer - MSFT26-Feb-06 13:30
memberTom Archer - MSFT26-Feb-06 13:30 
GeneralVC++ 2003 Pin
Snuckles27-Jun-05 5:53
memberSnuckles27-Jun-05 5:53 
GeneralRe: VC++ 2003 Pin
Nishant Sivakumar7-Dec-05 11:58
staffNishant Sivakumar7-Dec-05 11:58 
GeneralRe: VC++ 2003 Pin
mpodwyso6-Jun-06 13:39
membermpodwyso6-Jun-06 13:39 
GeneralDon't compile my project Pin
ronin20052001016-Jun-05 6:51
memberronin20052001016-Jun-05 6:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161208.2 | Last Updated 16 May 2005
Article Copyright 2005 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid