A question about global operators vs. polymorphism

D

Dean Roddey

I just upgraded the compiler version we use in our product (we'd stuck with
a very old one for a long time) and it seems that things have moved forward
while we were off in the wilderness, and I'm just wanting to find out
whether the new compiler is doing the right thing or not here... So it may
turn out to be a product specific quirk (MS Visual Studio 2005), but I
wanted to find out what the strictly correct C++ language answer is first.

Basically we have our own very large framework. We have the usual streaming
classes (base classes for in and out streams, from which various specific
streaming classes are derived for files, memory, etc...) and the usual
templatized collection classes. We want our collections to be streamable,
where the instantiation element supports it, so we provide the usual global
(friend) streaming operators with each collection type, so that
streamability isn't forced on the instantiation element types, something
like:

template <class T> TBinInStream&
operator>>(TBinInStream& strmToReadFrom, TFundStack<T>& colToStream)

template <class T> TBinOutStream&
operator<<(TBinOutStream& strmToWriteTo, const TFundStack<T>& colToStream)

Where TBinInStream and TBinOutStream are the base classes for in and out
streams. This worked fine before, but now that we've upgraded the compiler,
it refuses to use these and so we end up with unresolved symbole link errors
anywhere that we stream a collection. I can move these operators into the
collection classes and make them members, and it works fine, but then of
course it forces streamability on all elements that collections are
instantiated for, which isn't feasible.

Has something occured in the C++ language spec in the intervening years that
would make this not resolve when one of the parameters is polymorphic? Or is
VC++ just being stupid? Or is there some new syntax required to deal with
this kind of thing?

BTW, just for funzies, I set up a test where I did the actual streaming
calls via the base stream classes and it still refuses to see these
operators, so maybe it's not related to the stream being polymorphic. But I
can't see any other reason why it would fail to see these operators and use
them.

I have to admit that I've been head down in product develoment for a long
time and haven't been following the language evolution for the last four
years or so, so I may be way behind the times here. I'm asking because we
decided to do this upgrade before going out with our 2.0 release and now
this is throwing a monkey wrench in the works and I'm a bit time constrained
to figure out whether to punt and go back to the old system for now or to
try to move forward. I've done a lot of searching, but for something like
this it's hard to find the right set of web search keywords to find someone
asking the same question, so I've not come up with the (probably many)
previously answered versions of this question so far.
 
A

Alf P. Steinbach

* Dean Roddey:
something like:

See the FAQ item about how to post a question about code that doesn't work.

Essentially, post a minimal but complete example that compiles.
 
D

Dean Roddey

Given the nature of the question, no example that demonstrates the problem
is going to compile. And given that it's based on an enormous framework that
none of you have any access to, I'm not sure it would be very practical. But
here is a code outline that demonstrates the relevant parts:

//
// A templatized collection class with friend declarations for
// the streaming operators
//
template <class T> class TFundStack : public TObject, public MDuplicable
{
public :
.......

protected :
friend TBinOutStream& operator<<
(
TBinOutStream& strmOut
, const TFundStack<T>& fcolToStream
);

friend TBinInStream& operator>>
(
TBinInStream& strmIn
, TFundStack<T>& fcolToStream
);

}

// In the same header, global streaming operators
template <class T> TBinInStream&
operator>>(TBinInStream& strmToReadFrom, TFundStack<T>& colToStream)
{
}

template <class T> TBinOutStream&
operator<<(TBinOutStream& strmToWriteTo, const TFundStack<T>& colToStream)
{
}

* TBinInStream and TBinOutStream are the base classes from which various
types of binary in/out streams are derived.

// In a test app, I'd create a stack, put something in it
TFundStack<tCIDLib::TCard4> fcolStack(4);
fcolStack.Push(1);

// Then create an memory based output stream and stream the stack
TBinMBufOutStream strmTestOut(8192, 8192);
strmTest << fcolStack << kCIDLib::FlushIt;


The compiler doesn't find the global operators and I get an unresolved
symbol error on operator<<(TBinOutStream&, TFundStack<tCIDLib::TCard4>), so
it's not making the connection from the streaming of the stack to the
provided global streaming operator.
 
D

Dean Roddey

I found the answer. I just happened to eventually luck onto an indentical
question being asked by someone else. Basically the fix is that the friend
declarations needed to the parameterized. So this:

friend TBinOutStream& operator<<
(
TBinOutStream& strmOut
, const TFundStack<T>& fcolToStream
);

needed to become:

friend TBinOutStream& operator<< <T>
(
TBinOutStream& strmOut
, const TFundStack<T>& fcolToStream
);

I'm not sure if that is a VC++'ism that really isn't required by the
language itself, or if it always was required and my old VC++ version just
wasn't enforcing it and therefore I never understood the necessity for
parameterizing the friend declaration.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top