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

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • With the average hard drive now averaging one terabyte in size, the fallout from the explosion of user-created data has become an overwhelming volume of potential evidence that law-enforcement and corporate investigators spend countless hours examining. Join Us and SANS' Rob Lee for our 45-minute webinar, A Triage and Collection Strategy for Time-Sensitive Investigations, will demonstrate how to: Identify the folders and files that often contain key insights Reduce the time spent sifting through content by …

Most Popular Programming Stories

More for Developers

RSS Feeds