C++ standard library tips for VC6
Disclaimer: A lot of things here are only valid for VC6. Obviously,
a lot of people chose to stick with VC6 for Win32-only development,
therefore this document still holds some value for a lot of people.
Nowadays (20060505), I use VC7 for all STL related things, since VC6
is officially deprecated and doesn't compile with the latest Platform
SDK.
A lot of people prefer to use STLPort instead of the native STL
provided with VC6. At present (2005-06-26), STLPort 4.6.3 is not
"considered a good STL release". I had numerous issues, mostly related
to STLPort iostreams. Anyway it was no real showstopper (see the
next paragraph for one). Right now I use STLPort 5.0 from CVS because
I need new-style (new? The standard is called C++98 ;-) iostreams for
the socket++ library.
Also, std::string is really fast with STLPort. If you also happen to
use stringstreams, STLPort really shines.
The real main problem with native VC6 STL appears when using the STL
with multi-threaded programming. For normal usage it actually works
out quite nice. But the following code will kill VC6 within 10 seconds
on any machine I encountered - please note that the string is not
allocated on the heap or shared between threads.
/**
* @brief appending to string on the "stack" from multiple threads
*/
unsigned long __stdcall test(void *dummy_p) {
for (;;) {
std::string strTest;
for (int i=0;i<10;i++) {
strTest += "abcdef";
}
}
return 0;
}
/**
* @brief Main tester
*/
void main(void) {
int i;
DWORD thread_id;
for (i=0;i<100;i++)
CreateThread(NULL,0,test,(void *)i,0,&thread_id);
}
I am really glad somebody found this out on the usenet, see:
GoogleCrash . Basically you really do not want this kind of thing
to happen, and therefore if you are doing multithreaded
programming and using the STL with VC6, you need a fix.
Of course, this is not really helpful. Regarding STLPort again, I must
say that dinkumware provide a fixed library beginning at USD
135. After going through some "pain" in the STLport forums I think
this is an offer worth considering. But then again I personally am
totally annoyed of the fact that such a bug as above can happen and
therefore I am a bit reluctant to pay for a fix in a standard libary
bug what should be fixed anyway.
If you miss some CString functionality, try the class CStdString ,
available here which is a CString-alike wrapper above
std::string. Here are some other things you might find useful.
try setting #pragma warning(disable:4786) as early as possibly, if you
use precompiled headers, put it in there before including the standard
library headers.
#include <string>
#include <algorithm>
#include <cctype>
#include <cstdio>
// convert string to uppercase or lowercase
std::string sTesting;
std::transform( sTesting.begin(),
sTesting.end(),
sTesting.begin(),
(int(*)(int))::tolower );
This one allocates memory and uses std::vector and dynamic
allocation. This is not for high-performance usage. Anyway:
#include <string>
#include <vector>
#include <list>
#include <cstdarg>
inline std::string vstringprintf(const char* psFormat, va_list args)
{
std::vector<char> rgCharBuf(500);
int iRet = -1;
while (iRet == -1)
{
iRet = _vsnprintf( &rgCharBuf[0],
rgCharBuf.size() - 1,
psFormat,
args );
if (iRet == -1)
rgCharBuf.resize( rgCharBuf.size() * 2 );
}
rgCharBuf[rgCharBuf.size() - 1] = '\0';
return &rgCharBuf[0];
}
inline std::string stdprintf(char const *psFormat, ...)
{
va_list args;
va_start(args, psFormat);
std::string sRet = vstringprintf(psFormat, args);
va_end(args);
return sRet;
}
// use it like this
{
std::string sName = stdprintf( "The name is: %s, the age ist %d",
sName, lAge );
}
With no further interruption from my side:
#include <string>
#include <iostream>
using namespace std;
// ------------------------------------------------------------------
// --- Trim a std::string of trailing whitespaces (l&r) ---
// ------------------------------------------------------------------
static string TrimLeftStr( const string& sInStr, const string& sInChars )
{
const string::size_type iPos = sInStr.find_first_not_of( sInChars );
if ( iPos == std::string::npos )
return sInStr ;
return sInStr.substr( iPos );
}
static string TrimRightStr( const string& sInStr, const string& sInChars )
{
const string::size_type iPos = sInStr.find_last_not_of( sInChars );
if ( iPos == std::string::npos )
return sInStr ;
return sInStr.substr( 0, iPos + 1 );
}
static string TrimAll( const string& sInStr )
{
string sWhiteChar = " \t";
string sOutStr = TrimLeftStr( sInStr, sWhiteChar );
sOutStr = TrimRightStr( sOutStr, sWhiteChar );
return sOutStr;
}
// testing
int main(int argc, char* argv[])
{
string sTest = "\t\t\t This parrot is dead. \t";
string sOutput = TrimAll( sTest );
cout << ">" + sOutput + "<";
return 0;
}
This is not strictly regarding the c++ standard library but sometimes
still relevant and therefore I mention it. In fact I really hate
doing this, but sometimes it is a requirement to have a C API.
The C++ ABI (application binary interface) is not standartized on a
given platform, as you probably know. Therefore linking C++ DLLs from
two different compiler vendors without sourcecode is problematic. One
fundamental approach to this problem (and DLL Hell, and binary
components in C++) is described in Don Box's essential COM reading
called Essential COM (guess what?) . Read the first chapter and you
get the whole thing (basically exporting ABCs with pure virtual
functions). If the overall approach described in that chapter is not
feasible for you or you just really need the plain straight C API you
can create an C-API wrapper.
This is the class we would like to export from a DLL in a
straightforward way.
/// a simple class
class CCarEngine
{
public:
CCarEngine(void);
virtual ~CCarEngine();
// --- methods ---
public:
void Enter();
void Leave();
void Start();
void Drive( const char* psDest );
};
This is the header file the C users will eventually get (all the C
people will get is this header file and the DLL (import library +
binary ):
#ifndef _CARENGINE_H_
#define _CARENGINE_H_
/* win32 export directives */
#ifdef CARENGINE_EXPORTS
# define CARENGINE_API __declspec(dllexport)
#else
# define CARENGINE_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/*
* the glorious engine api
*/
/* known types */
struct CCarEngine;
typedef CCarEngine* HENG;
/* operations on engines */
CARENGINE_API HENG CarEng_New ( );
CARENGINE_API void CarEng_Del ( HENG pEng );
CARENGINE_API void CarEng_Enter ( HENG pEng );
CARENGINE_API void CarEng_Leave ( HENG pEng );
CARENGINE_API void CarEng_Start ( HENG pEng );
CARENGINE_API void CarEng_Drive ( HENG pEng, char* psDest );
#ifdef __cplusplus
}
#endif
#endif
As you can see the basic win32 C-DLL skeleton makes up for a lot of
these lines here, not our actual API. The basic Idea is to forward
declare the C++ class in this exported C Header File (CarEngine.h) as
a struct. Now we supply the implementation, and it looks like this:
#include "CarEngine.h" // the C API forward declares this class
// the real class
struct CCarEngine // note: using struct here
{
public:
CCarEngine(void);
virtual ~CCarEngine();
// --- methods ---
public:
void Enter() {}
void Leave() {}
void Start() {}
void Drive( const char* psDest ) {}
};
// ------------------------------------------------------------------
// --- cumbersome wrapping ---
// ------------------------------------------------------------------
CARENGINE_API HENG CarEng_New ( ) { return new CCarEngine(); }
CARENGINE_API void CarEng_Del ( HENG pEng ) { delete pEng; }
CARENGINE_API void CarEng_Enter ( HENG pEng ) { pEng->Enter(); }
CARENGINE_API void CarEng_Leave ( HENG pEng ) { pEng->Leave(); }
CARENGINE_API void CarEng_Start ( HENG pEng ) { pEng->Start(); }
CARENGINE_API void CarEng_Drive ( HENG pEng, char* psD) {pEng-Drive(psD);}
Yeah, this really is ackward but straightforward and simply solves the
problem which I thought had gone down the drain 10 years ago in a
mechanical, systematic way. The sample above needs some checking of
its input values (assert or whatever) at the C-API function level, but
it was skipped here for the sake of brevity. With this approach you
can still design and think in C++ and export your types as ADTs ;-) to
the C programming language.
More later
Good bye
top