Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with stream interface in C++

So I have a virtual streaming interface in c++

class KxStream
{
public:
   virtual KxStream& operator<< ( u32 num ) = 0;
};

It has a ton of basic << operators for all the built-in types. I've just listed one.

Then I have a few classes that implement the streaming interface. Like this:

class KxCbuf : public KxStream
{
public:
   KxStream& operator<<( u32 num );
}

So there is an implementation of the streaming interface in KxCbuf. So far so good. Then I have some classes that overload the stream interface:

class KxSymbol
{
   operator u32() const;
   friend KxStream& operator<<( KxStream& os, KxSymbol sym );
};

Note that this class has cast operator to a built-in type. Now when I try to stream one of these classes into one of the classes that implements the streaming interface I get an error:

KxCbuf buf;
KxSymbol sym;

buf << sym; // error!

The compiler gets confused about which functions to use. Gcc compiles fine, but says that there are multiple ways do this this. MSVC does not compile saying there are multiple overloads:

src/variable.cpp(524) : error C2666: 'KxCbuf::operator <<' : 15 overloads have similar conversions

I know what is happening, just not how to solve it satisfactorily. So the compiler can either cast KxCbuf -> KxStream, and then call the friend function, which is the right thing to do. Or it can cast KxSymbol -> u32, and then call the u32 << operator in KxCbuf, inherited from KxStream.

I can solve it two (bad) ways.

I can start by streaming in something unambiguous:

buf << "" << sym;

This way the return value of the first stream operator for "" return KxStream, and all is well. Or I can implement a redundant stream operator for the implementation class. E.g. I can add the following to KxSymbol:

friend KxStream& operator<<( KxCbuf& os, KxSymbol sym );

The first answer always works - but it sure is ugly. The second answer is also ugly, in that I have to create redundant stream operators, and does not always work in that the KxStream implementations are not always visible in places where I need to define new stream operators.

Ideally I'd like implementations of the KxStream interface to work just like KxStream objects, and avoid implicit casts that cause ambiguous conversions.

How do I solve this?

(ps. I need to create my own streaming operators for a custom serialization scheme for my library. I can't use boost or similar third party libraries that have their own serialization classes )

@Edit: There are several good answers related to controlling the compiler's use of the implicit conversion, like the conversion to KxSymbol -> u32, unfortunately that implicit conversion is important to the code. For example KxSymbol is a class that stores strings in a table and returns them as numbers so that I can compare strings as numbers. E.g. if two symbols are not equal, then the strings are not the same. I also store symbols as numbers in some data structures.

Is there a way to solve this from the other side, somehow make the compiler understand that implementations of KxStream should be cast to KxStream objects by preference to other implicit casts?

For example what if I would somehow force the compiler to have to first cast KxCbuf to KxStream before using the operator<< for the built-in types. This would make it always prefer the overload operator<<'s over the KxStream ones. - the overloads would require one cast, and the KxStream ones would require two.

like image 796
Rafael Baptista Avatar asked Aug 13 '12 14:08

Rafael Baptista


2 Answers

If you have a C++11 compiler, mark the conversion function "explicit". That way you won't get implicit conversions to u32, and this sort of ambiguity will disappear.

like image 57
Pete Becker Avatar answered Oct 13 '22 21:10

Pete Becker


The simplest and safest way is to change your implicit conversion from operator u32() const; to a named method u32 as_u32() const. Not only does this remove the ambiguity it can prevent all sorts of unwanted accidental conversions that will get you in trouble in the future.

like image 24
Mark B Avatar answered Oct 13 '22 21:10

Mark B