#include "stdafx.h"
#include <strstream>
using namespace std;
//---------------------------------------------------------
--------------------
typedef std::ostrstream seccStream;
class seccStreamStr {
public:
seccStreamStr(seccStream &s /* don't use
stream after this call ! */)
{
s << std::ends;
fStr = s.str(); //
seccStreamStr is now owner of string
}
const char* Get() const
{ return fStr; }
~seccStreamStr()
{ delete [] fStr; }
private:
seccStreamStr(const seccStreamStr
&); // disable
seccStreamStr& operator=(const
seccStreamStr &); // disable
char* fStr;
};
//---------------------------------------------------------
--------------------
class seccMem {
public:
seccMem(const char* s){fStart=s;}
const char* fStart;
};
//---------------------------------------------------------
--------------------
std::ostream& operator<<(std::ostream &out, const seccMem
&m)
{
// write as string (without quotes); no formating
return out << m.fStart;
}
//---------------------------------------------------------
--------------------
//#define DO_COMPILER_PROBLEM_7_1_WORKAROUND
#ifdef DO_COMPILER_PROBLEM_7_1_WORKAROUND
const seccMem dummyFirstStreamParameter("");
#define seccStreamMake(ssParam) (seccStream&)
(seccStream() << dummyFirstStreamParameter << ssParam)
#else
#define seccStreamMake(ssParam) (seccStream&)
(seccStream() << ssParam)
#endif
//---------------------------------------------------------
--------------------
int _tmain(int argc, _TCHAR* argv[])
{
const char* pszDirName="c:\\develop\\";
const char* fileName="seccStream.cpp";
seccStream ssDirFileName1;
ssDirFileName1 << pszDirName << fileName; //
expands correctly to c:\Develop\seccStream.cpp
seccStreamStr ssDirFileName2=seccStreamMake
(pszDirName << fileName); // expands to
00326A29seccStream.cpp
return 0;
}
>/*
>The follwing macro seccStreamMake() does not expand
>correctly with Visual Studio 2003 C++ compiler.
>The compiler streams the first macro parameter pszDirName
>as a void* pointer instead of a const char*.
>For some reason the problem does not occur with the first
>macro parameter of the type seccMem,
>perhaps because seccMem has an overloaded stream operator.
>Streaming the first dummy parameter of the type seccMem
>overcomes the compiler drawback.
Incorrectly even with the workaround. The problem is a non-conforming
extension that the compiler has - try compiling with extensions turned
off.
>
>//#define DO_COMPILER_PROBLEM_7_1_WORKAROUND
>
>#ifdef DO_COMPILER_PROBLEM_7_1_WORKAROUND
>
> const seccMem dummyFirstStreamParameter("");
>
> #define seccStreamMake(ssParam) (seccStream&)
>(seccStream() << dummyFirstStreamParameter << ssParam)
The above code is in fact illegal - you attempt to call
operator<<(ostream&, seccMem const&)
with the first parameter a temporary object. You've forgotten that it
is illegal to bind a non-const reference to a temporary object.
Compile with extensions turned off or on warning level 4 to see the
error.
>#else
>
> #define seccStreamMake(ssParam) (seccStream&)
>(seccStream() << ssParam)
>
>#endif
>
>
>//---------------------------------------------------------
>--------------------
>
>int _tmain(int argc, _TCHAR* argv[])
>{
> const char* pszDirName="c:\\develop\\";
>
> const char* fileName="seccStream.cpp";
>
> seccStream ssDirFileName1;
> ssDirFileName1 << pszDirName << fileName; //
> expands correctly to c:\Develop\seccStream.cpp
>
> seccStreamStr ssDirFileName2=seccStreamMake
>(pszDirName << fileName); // expands to
>00326A29seccStream.cpp
Here the Microsoft extension is interfering with the language in a
slightly odd way. Because the void* operator<< is a member function of
ostream, it is the only overload that can be legally called for the
above code by a standard compiler. However, given the microsoft
extension to reference binding, you'd expect the char* overload (which
requires the extension to be chosen, since it is a non-member
operator<<) to be chosen.
Anyway, compile on warning level 4 and avoid using extensions where
possible. Here's a slighly immoral looking but standards conforming
version of your macro:
template <class T>
inline T& temp_ref(T const& t)
{
return const_cast<T&>(t);
}
#define seccStreamMake(ssParam) (seccStream&)(temp_ref(seccStream())
<< ssParam)
Now you bind the temporary to a const reference, which is fine, but
the turn it back into the required non-const reference using a cast.
However, this whole idea of using a temporary strstream isn't great
because of lifetime issues (the code you produced seems rather fragile
and liable to leak memory, etc.).
The following code would be better IMHO:
#include "stdafx.h"
#include <sstream>
#include <ostream>
#include <string>
typedef std::ostringstream seccStream;
template <class T>
T& temp_ref(T const& t)
{
return const_cast<T&>(t);
}
#define stringMake(ssParam)
static_cast<seccStream&>(temp_ref(seccStream()) << ssParam).str()
#include <iostream>
int main()
{
const char* pszDirName="c:\\develop\\";
const char* fileName="seccStream.cpp";
std::string ssDirFileName2 = stringMake(pszDirName << fileName);
std::cout << ssDirFileName2 << '\n';
}
Tom