Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

wrong operator<<() overload in macro expansion with Visual Studio 2003 C++

1 view
Skip to first unread message

Marcel Baumann

unread,
Sep 30, 2003, 3:53:09 AM9/30/03
to
/*
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.
*/
// seccStream.cpp : Defines the entry point for the
console application.
//

#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;
}


tom_usenet

unread,
Sep 30, 2003, 6:03:29 AM9/30/03
to
On Tue, 30 Sep 2003 00:53:09 -0700, "Marcel Baumann"
<marcel....@ecofin.ch> wrote:

>/*
>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

0 new messages