inquam
inquam

Reputation: 12942

Ambiguity when writing TStringList to TStream

I'm working on a project that sends data between clients using streams. It implements a bunch of stream operators to support this. It worked fine in C++ Builder 2010 but now that I'm moving the project to C++ Builder 10 Seattle I get a weird error.

In one function a TStream is created and data written to it

*pStream >>
        Version >>
        m_iPortNumber >>
        m_iHeartBeatTimeout >>
        m_iMaxToRead >>
        m_pIPAddressFilter >>
        m_iRetries;

m_pIPAddressFilter here is a TStringList and the error is related to that. It reads

[bcc32 Error] E2015 Ambiguity between '>>(System::Classes::TStream &,bool &) at Common\Streams.h:28' and '>>(System::Classes::TStream &,System::Classes::TStrings *) at Common\Streams.h:42'

The implemented stream operators it's referring to look like this

PACKAGE TStream & operator <<(TStream &Stream, bool b)
{
    Stream.WriteBuffer(&b, sizeof(b));

    return Stream;
}

PACKAGE TStream & operator >>(TStream &Stream, bool &b)
{
    Stream.ReadBuffer(&b, sizeof(b));

    return Stream;
}

PACKAGE TStream & operator <<(TStream &Stream, TStrings *pList)
{
    int Count;
    int i;

    Stream << (Count=pList->Count);
    for (i=0; i<Count; i++)
        Stream << static_cast<WideString> (pList->Strings[i]);

    return Stream;
}

PACKAGE TStream & operator >>(TStream &Stream, TStrings *pList)
{
    AnsiString s;
    int Count;

    pList->Clear();
    Stream >> Count;
    while (Count--)
    {
        Stream >> s;
        pList->Add(s);
    }

    return Stream;
}

How can the compiler be confused by this and feel that it's ambiguous if the version taking a bool or the version taking TStrings should be used. TStrings is even a parent class of TStringList that is what is being written to the stream. As said, this worked fine in C++ Builder 2010.

Upvotes: 0

Views: 310

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598309

You don't have an >> operator defined specifically for TStringList*, so the compiler has to look for matching candidates to convert a TStringList* pointer to. Any pointer is implicitly convertible to a bool, and it would seem that the compiler considers a compiler-created temporary bool variable is a viable overload candidate, thus an ambiguity since TStringList* is convertible to both TStrings* and bool& (via a temp).

Change your TStrings* operators to take TStrings& parameter instead (which you should do anyway, don't pass object pointers to streaming operators), and then change m_pIPAddressFilter to *m_pIPAddressFilter when invoking the operators. Then there will not be any ambiguity.

*pStream >>
    ...
    *m_pIPAddressFilter
    ...;

On a separate note, your TStrings operators seem to be inconsistent. << is streaming out UnicodeString values using an intermediate WideString (Why? And why are you using static_cast?). Your >> operator is streaming in UnicodeString values using an intermediate AnsiString instead. Those are different data conversions, so you are likely to corrupt data when non-ASCII characters are present. You need to use consistent string types for both operators. You should be streaming the TStrings values as-is using String variables, and then implement String operators to handle the actual streaming using a consistent format, like UTF-8, eg:

PACKAGE TStream & operator <<(TStream &Stream, String s)
{
    UTF8String utf = s;
    int Count = utf.Length();
    Stream << Count;
    Stream.WriteBuffer(utf.c_str(), Count);
    return Stream;
}

PACKAGE TStream & operator >>(TStream &Stream, String &s)
{
    UTF8String utf;
    int Count;
    Stream >> Count;
    s.SetLength(Count);
    Stream.ReadBuffer(utf.c_str(), Count);
    s = utf;
    return Stream;
}

PACKAGE TStream & operator <<(TStream &Stream, TStrings &pList)
{
    int Count = pList.Count;
    Stream << Count;
    for (int i = 0; i < Count; ++i)
        Stream << pList.Strings[i];
    return Stream;
}

PACKAGE TStream & operator >>(TStream &Stream, TStrings &pList)
{
    pList.BeginUpdate();
    try
    {
        pList.Clear();

        String s;
        int Count;

        Stream >> Count;
        while (Count--)
        {
            Stream >> s;
            pList.Add(s);
        }
    }
    __finally {
        pList.EndUpdate();
    }

    return Stream;
}

Upvotes: 0

Related Questions