CBStr: a BSTR wrapper-class with string manipulation members

The MFC CString class, has some really nice string manipulation routines. However, if you are trying to avoid the MFC for size/deployment issues, and having to do string manipulation of BSTRs I'm sure that you will be finding the _bstr_t class frustratingly basic.

After doing a great deal of frustrated hair-pulling I finally got around to implementing this class, CBstr. It is a C++ class derived from the MS VC++ _bstr_t COM support class with the added benefit of a barrow-load of string manipulation methods. The original idea came after seeing Alex Stockton's NoMFC::CString class at www.WorldofATL.com. This class was used as the template for the public interface.

The class uses the same public "interface" as the NoMFC::CString Class with a couple of minor differences and as a result has all the functionality of the "real" (MFC) CString class with a few extra's. And because it is derived from _bstr_t

Because the class is derived from _bstr_t it is very easy to use instead of that class, especially where string manipulation of any kind is required. If you are not planning on using the string-manipulation functionality I would suggest sticking with the _bstr_t class for reduced class size.

To use the class simply #include the CBstrImpl.h header file supplied in the Zip, and have this and CBStr.h in your include path.

A demo program (and my test rig) follows and is included in the zip file. This exercises all of the functionality of of the class, but for more information I would suggest looking at the documentation of the MFC CString class - it doesn't seem woth copying it out ;-)

This code has been tested with Visual C++ 5.0 Service Pack 3, it has not been tested with Visual C++ 6.0

Example code



// Main.cpp
//
// Test harness to execise the CBstr class
// derived from _bstr_t and based on the 
// NoMFC::CString class from www.worldofatl.com
//
// If anyone can figure out why the ostream::operator<< refuses to
// use the implicit cast to const char* or char* then please email me 
// at gary.olliffe@parallax.co.uk
// 
//
#include "CBstrImpl.h"
#include <iostream>

using std::cout;

const TCHAR* Test(const TCHAR* psz);

// This test only works in ANSI (not UNICODE) builds
// due to lack of _T() and stream insertion operator

int main()
{
	// Create a proper BSTR 
	BSTR bstr = ::SysAllocString(L"Hello World");

	// Create two objects of out new class
	CBstr newstr(bstr, true);    //Take a copy of bstr
	CBstr newstr2(bstr, false);  //attach to bstr

	// test the MakeUpper method
	newstr2.MakeUpper();       

	// Extract the BSTR (this gives a reference 
	// to the encapulated BSTR, not a copy)
	BSTR ptrcopy = newstr2;

	// Take a copy of the encapsulated BSTR 
	BSTR deepcopy = newstr2.copy();

	// Test cast to char *
	char * ansistr = newstr2;

	// Test cast to const char *
	const char* ansicstr = newstr2;

	// output results 	
	cout << ansistr << "\n" << ansicstr;
	cout << newstr2;

	// Create another string for testing
	CBstr str("twenty");

	// Call a method that takes a const char* parameter
	Test(str);

	// create another CBstr from the first
	CBstr str2(str);

	// output results
	cout << (const char*)str << "\n";
	cout << (const char*)str2 << "\n";

	// test the format method  
	str.Format("sder %d %s", 1, "ss");
	cout << (const char*)str << "\n";

	// test the length() method
	CBstr s("abcdef");
	cout << s.length() << "\n"; // == 6 

	// test the isempty() method
	CBstr s2;
	cout << s2.IsEmpty() << "\n"; // == true

	// test the Empty method
	CBstr s3("abc");
	s3.Empty();
	cout << s3.length() << "\n"; // == 0

	// Test GetAt() method
	CBstr s4("abcdef");
	cout << s4.GetAt(2) << "\n"; //  == 'c'

	// Test the operator[]
	CBstr s5("abc");
	cout << s5[1] << "\n"; // == 'b'
	s5.SetAt(1, 'X');
	cout << s5[1] << "\n"; // == 'X'


	// test assignment operators
	CBstr s6, s7;        // Empty CBstr objects
	s6 = "cat";            // s6 == "cat"
	cout << (const char*)s6 << "\n";
	s7 = s6;               // s6 and s7 each == "cat"
	cout << (const char*)s7 << "\n";

	// test operator+()
	s6 = "the " + s6;      // Or expressions
	cout << (const char*)s6 << "\n";

	// example for CBstr::operator +
	CBstr s8("abc");
	CBstr s9("def");
	cout << (const char*)(s8 + s9 ) << "\n"; // == "abcdef"
	CBstr s10;
	s10 = CBstr("abc") + "def" ; // == "abcdef"
	cout << (const char*)s10 << "\n";

	// example for CBstr::operator +=
	CBstr s11("abc");
	cout << (const char*)( s11 += "def") << "\n"; // == "abcdef"

	// example for CBstr Comparison Operators
	CBstr s12("abc");
	CBstr s13("abd");
	if (s12 < s13)
		cout << "s12 < 13" << "\n"; // == true Operator is overloaded for both.
	if ("ABC" < s12)
		cout << "ABC < s12" << "\n"; // == true CBstr and char*
	if (s13 > "abe")
		cout << "s13 > abe" << "\n"; // == false 
	if (s13 == "abd")
		cout << "s13 == abd" << "\n"; // == true

	// example for CBstr::Compare
	CBstr s14( "abc" );
	CBstr s15( "abd" );
	cout << s14.Compare(s15) << "\n"; // == -1 // Compare with another CBstr.
	cout << s14.Compare("abe") << "\n"; // == -1 // Compare with LPTSTR string.

	// example for CBstr::CompareNoCase
	CBstr s16( "abc" );
	CBstr s17( "ABD" );
	cout << s16.CompareNoCase( s17 ) << "\n"; // == -1 // Compare with a CBstr.
	cout << s16.Compare( "ABE" )  << "\n"; // == 1 // Compare with LPTSTR string.

	// example for CBstr::Mid
	CBstr s18("abcdef");
	cout << (const char*)s18.Mid(2, 3) << "\n"; //  == _T("cde")

	// example for CBstr::Left
	CBstr s19( _T("abcdef") );
	cout << (const char*)s19.Left(2) << "\n"; //  == _T("ab")

	// example for CBstr::Right
	CBstr s20( _T("abcdef") );
	cout << (const char*)s20.Right(2) << "\n"; //  == _T("ef")

	// example for CBstr::SpanIncluding
	CBstr s21( "cabbage" );
	CBstr s22 = s21.SpanIncluding( "abc" );
	cout << (const char*)s22 << "\n"; //  == "cabba"
	s22 = s21.SpanIncluding( "xyz" );
	cout << s22.IsEmpty( ) << "\n"; // == true

	// example for CBstr::MakeUpper
	CBstr s23( "abc" );
	s23.MakeUpper();
	cout << (const char*)s23 << "\n"; //  == "ABC"

	// example for CBstr::MakeLower
	CBstr s24( "ABC" );
	s24.MakeLower();
	cout << (const char*)s24 << "\n"; //  == "abc"

	// example for CBstr::MakeReverse
	CBstr s25( "abc" );
	s25.MakeReverse();
	cout << (const char*)s25 << "\n"; //  == "cba"

	// example for CBstr::Find
	CBstr s26( "abcdef" );
	cout << s26.Find( 'c' ) << "\n"; //  == 2
	cout << s26.Find( "de" ) << "\n"; //  == 3

	// example for CBstr::ReverseFind
	CBstr s27( "abcabc" );
	cout << s27.ReverseFind( 'b' ) << "\n"; //  == 4

	// example for CBstr::FindOneOf
	CBstr s28( "abcdef" );
	cout << s28.FindOneOf( "xd" ) << "\n"; //  == 3 // 'd' is first match

	return 0;
}


Download demo project - 7 KB



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Live Event Date: December 18, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this upcoming webcast …

Most Popular Programming Stories

More for Developers

RSS Feeds