yandex / ozo

OZO is a C++17 Boost.Asio based header-only library for asyncronous communication with PostgreSQL DBMS.
PostgreSQL License
226 stars 45 forks source link

Ambiguous call to size function #228

Closed jonesmz closed 4 years ago

jonesmz commented 4 years ago

I have a class:

class BasicSharedString : public std::basic_string_view<CHAR_T>
                        , private boost::shared_ptr<CHAR_T[]>
{ /* ... */ };

I've provided an adapter for this class to OZO.

OZO_PG_BIND_TYPE(meshpp::core::SharedString, "text");

When I use it in a query, I get this error:

/home/jonesmz/meshpp_frameworks/3rdparty/ozo/include/ozo/impl/write.h:31: error: call to 'size' is ambiguous out.write(data(in), size(in)); ^~~~ From this function

template <typename T>
inline Require<RawDataReadable<T>, ostream&> write(ostream& out, const T& in) {
    using std::data;
    using std::size;
    out.write(data(in), size(in));
    if (!out) {
        throw system_error(error::unexpected_eof);
    }
    return out;
}

And these candidate functions:

One from standard library

/usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/include/g++-v9/bits/range_access.h:242: candidate function [with _Container = meshpp::core::BasicSharedString] size(const _Container& cont) noexcept(noexcept(cont.size())) ^

  template <typename _Container>
    constexpr auto
    size(const _Container& __cont) noexcept(noexcept(__cont.size()))
    -> decltype(__cont.size())
    { return __cont.size(); }

One from Boost.

/usr/include/boost/range/size.hpp:55: candidate function [with SinglePassRange = meshpp::core::BasicSharedString] size(const SinglePassRange& rng) ^

    template<class SinglePassRange>
    inline typename range_size<const SinglePassRange>::type
    size(const SinglePassRange& rng)
    {
// Very strange things happen on some compilers that have the range concept
// asserts disabled. This preprocessor condition is clearly redundant on a
// working compiler but is vital for at least some compilers such as clang 4.2
// but only on the Mac!
#if BOOST_RANGE_ENABLE_CONCEPT_ASSERT == 1
        BOOST_RANGE_CONCEPT_ASSERT((boost::SinglePassRangeConcept<SinglePassRange>));
#endif

#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) && \
    !BOOST_WORKAROUND(__GNUC__, < 3) \
    /**/
        using namespace range_detail;
#endif

        return range_calculate_size(rng);
    }

I can work around this problem by providing the function "size" in the same namespace as my custom type.

constexpr const char* data(SharedString const& str)
{
    return str.data();
}

constexpr std::size_t size(SharedString const& str)
{
    return str.size();
}

But I would really rather not have to do this.

Is this the intended behavior of OZO? If not, can any adjustments be made?

thed636 commented 4 years ago

Hi, Michael!

The workaround that you mentioned is one of the ways how it may be solved, since nobody but you can decide which implementation of size() should be used. As an alternative, you can try using std::basic_string_view<CHAR_T>::size statement inside your type definition to tell the compiler which function should be called. The other way is to use the dedicated customization point of the library - ozo::send_impl template specialization which in your case just should be inherited from ozo::send_impl<std::basic_string_view<CHAR_T>> and the conflict shall be resolved via implicit cast of BasicSharedString to std::basic_string_view<CHAR_T>.

I hope that helps. With best regards Sergei

thed636 commented 4 years ago

BTW, Michael, why did you decide inheritance

class BasicSharedString : public std::basic_string_view<CHAR_T>
                        , private boost::shared_ptr<CHAR_T[]>
{ /* ... */ };

instead of aggregation:

class BasicSharedString : public std::basic_string_view<CHAR_T>
{ 
    boost::shared_ptr<CHAR_T[]> buf_;
/* ... */ };

?

jonesmz commented 4 years ago

No meaningful reason.

It does help slightly with the order of initialization for the member variables and base classes though.