Loki-Astari / ThorsSerializer

C++ Serialization library for JSON

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Heavy usage of unique_ptr introduces problem

opened this issue · comments

Hi, as almost all properties of the JSON body I get within a REST webservice are optional I heavily use unique_ptr. This results in a compiler error. I guess because the line

object = PolyMorphicRegistry::getNamedTypeConvertedTo<BaseType>(className);

within tryParsePolyMorphicObject tries to copy a unique_ptr where it should move it instead.

Here is an example:

#include <iostream>

#include <ThorSerialize/JsonThor.h>
#include <ThorSerialize/SerUtil.h>

using namespace ThorsAnvil::Serialize;
using namespace std;

struct Quantities {
    std::vector<int>* quantities;

    ~Quantities()
    {
        delete quantities;
    }
};

ThorsAnvil_MakeTrait(Quantities, quantities);

struct AbstractTourResult {
    std::unique_ptr<Quantities> maxQuantities { nullptr };
    virtual ~AbstractTourResult() = default;
    ThorsAnvil_PolyMorphicSerializer(AbstractTourResult);
};
ThorsAnvil_MakeTrait(AbstractTourResult, maxQuantities);

struct TourResult : public AbstractTourResult {
    ~TourResult() override = default;
    ThorsAnvil_PolyMorphicSerializer(TourResult);
};
ThorsAnvil_ExpandTrait(AbstractTourResult, TourResult);

struct Tour {
    std::unique_ptr<TourResult> result { nullptr };
};
ThorsAnvil_MakeTrait(Tour, result);

int main()
{
    Tour t {};
    string str {};
    istringstream(str) >> ThorsAnvil::Serialize::jsonImport(t);

    return 0;
}

Error:

In file included from /Users/ivhedtke/CLionProjects/testthor/main.cpp:3:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/JsonThor.h:14:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/JsonParser.h:20:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.h:393:
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:251:12: error: no viable overloaded '='
    object = PolyMorphicRegistry::getNamedTypeConvertedTo<BaseType>(className);
    ~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:304:13: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::tryParsePolyMorphicObject<std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >' requested here
            tryParsePolyMorphicObject(parent, parser, object, 0);
            ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:412:22: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializationForBlock<ThorsAnvil::Serialize::TraitType::Pointer, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >::scanObject' requested here
        deserializer.scanObject(object);
                     ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:396:5: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializeMemberValue<Tour, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> >, ThorsAnvil::Serialize::TraitType::Pointer>::init' requested here
    init(parent, parser, key, memberInfo.first, object.*(memberInfo.second));
    ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:433:12: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializeMemberValue<Tour, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> >, ThorsAnvil::Serialize::TraitType::Pointer>::DeSerializeMemberValue' requested here
    return DeSerializeMember<T, M>(parent, parser, key, object, memberInfo);
           ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:441:51: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::make_DeSerializeMember<Tour, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >' requested here
    CheckMembers memberCheck = {static_cast<bool>(make_DeSerializeMember(*this, parser, key, object, std::get<Seq>(member)))...};
                                                  ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:448:12: note: (skipping 2 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
    return scanEachMember(key, object, members, std::make_index_sequence<sizeof...(Members)>());
           ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:146:29: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::DeSerializer::scanObjectMembers<Tour, std::__1::basic_string<char> >' requested here
                if (!parent.scanObjectMembers(key, object))
                            ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:474:15: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializationForBlock<ThorsAnvil::Serialize::TraitType::Map, Tour>::scanObject' requested here
        block.scanObject(object);
              ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Importer.h:34:30: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::DeSerializer::parse<Tour>' requested here
                deSerializer.parse(data.value);
                             ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/istream:1440:10: note: in instantiation of member function 'ThorsAnvil::Serialize::operator>>' requested here
    __is >> _VSTD::forward<_Tp>(__x);
         ^
/Users/ivhedtke/CLionProjects/testthor/main.cpp:42:24: note: in instantiation of function template specialization 'std::__1::operator>><char, std::__1::char_traits<char>, ThorsAnvil::Serialize::Importer<ThorsAnvil::Serialize::Json, Tour> >' requested here
    istringstream(str) >> ThorsAnvil::Serialize::jsonImport(t);
                       ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2347:28: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > *' to 'const std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> >' for 1st argument; dereference the argument with *
class _LIBCPP_TEMPLATE_VIS unique_ptr {
                           ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2463:15: note: candidate function not viable: no known conversion from 'std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > *' to 'std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> >' for 1st argument; dereference the argument with *
  unique_ptr& operator=(unique_ptr&& __u) _NOEXCEPT {
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2555:15: note: candidate function not viable: no known conversion from 'std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > *' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
  unique_ptr& operator=(nullptr_t) _NOEXCEPT {
              ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2474:15: note: candidate template ignored: could not match 'unique_ptr<type-parameter-0-0, type-parameter-0-1>' against 'std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > *'
  unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) _NOEXCEPT {
              ^
In file included from /Users/ivhedtke/CLionProjects/testthor/main.cpp:3:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/JsonThor.h:14:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/JsonParser.h:20:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.h:34:
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Traits.h:445:46: error: no type named 'Root' in 'ThorsAnvil::Serialize::Traits<std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >'
            using Root = typename Traits<T>::Root;
                         ~~~~~~~~~~~~~~~~~~~~^~~~
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:251:35: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::PolyMorphicRegistry::getNamedTypeConvertedTo<std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >' requested here
    object = PolyMorphicRegistry::getNamedTypeConvertedTo<BaseType>(className);
                                  ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:304:13: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::tryParsePolyMorphicObject<std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >' requested here
            tryParsePolyMorphicObject(parent, parser, object, 0);
            ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:412:22: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializationForBlock<ThorsAnvil::Serialize::TraitType::Pointer, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> > >::scanObject' requested here
        deserializer.scanObject(object);
                     ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:396:5: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializeMemberValue<Tour, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> >, ThorsAnvil::Serialize::TraitType::Pointer>::init' requested here
    init(parent, parser, key, memberInfo.first, object.*(memberInfo.second));
    ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:433:12: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializeMemberValue<Tour, std::__1::unique_ptr<TourResult, std::__1::default_delete<TourResult> >, ThorsAnvil::Serialize::TraitType::Pointer>::DeSerializeMemberValue' requested here
    return DeSerializeMember<T, M>(parent, parser, key, object, memberInfo);
           ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:441:51: note: (skipping 3 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
    CheckMembers memberCheck = {static_cast<bool>(make_DeSerializeMember(*this, parser, key, object, std::get<Seq>(member)))...};
                                                  ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:146:29: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::DeSerializer::scanObjectMembers<Tour, std::__1::basic_string<char> >' requested here
                if (!parent.scanObjectMembers(key, object))
                            ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.tpp:474:15: note: in instantiation of member function 'ThorsAnvil::Serialize::DeSerializationForBlock<ThorsAnvil::Serialize::TraitType::Map, Tour>::scanObject' requested here
        block.scanObject(object);
              ^
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Importer.h:34:30: note: in instantiation of function template specialization 'ThorsAnvil::Serialize::DeSerializer::parse<Tour>' requested here
                deSerializer.parse(data.value);
                             ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/istream:1440:10: note: in instantiation of member function 'ThorsAnvil::Serialize::operator>>' requested here
    __is >> _VSTD::forward<_Tp>(__x);
         ^
/Users/ivhedtke/CLionProjects/testthor/main.cpp:42:24: note: in instantiation of function template specialization 'std::__1::operator>><char, std::__1::char_traits<char>, ThorsAnvil::Serialize::Importer<ThorsAnvil::Serialize::Json, Tour> >' requested here
    istringstream(str) >> ThorsAnvil::Serialize::jsonImport(t);
                       ^
In file included from /Users/ivhedtke/CLionProjects/testthor/main.cpp:3:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/JsonThor.h:14:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/JsonParser.h:20:
In file included from /usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Serialize.h:34:
/usr/local/Cellar/thors-serializer/1.10.1/include/ThorSerialize/Traits.h:455:20: error: 'int' is not a class
            return dynamic_cast<T*>(dataBase);
                   ^                ~~~~~~~~
2 warnings and 3 errors generated.

So far, as a work around I use plain pointers instead of unique_ptr.

Be careful of this:

struct Quantities {
    std::vector<int>* quantities;

    ~Quantities()
    {
        delete quantities;
    }
};

This class does not obey the rule of three (watch out for copy/move operations generated by the compiler).

But additionally you don't need to use a pointer here. Simply use std::vector<int>. If there is no quantities data in the json then you will get an empty vector.

This is also a problem:

    istringstream(str) >> ThorsAnvil::Serialize::jsonImport(t);

The trouble is that istringstream(str) creates a temporary object. When this is passed to operator>>() it can only be passed as a const reference. This will generate compiler errors. To get this to compile you need to do:

    istringstream    stream(str);
    stream >> ThorsAnvil::Serialize::jsonImport(t);

The latest version should work for you.
Thanks for the bug report.

Would love to know what you are building and happy to continue fixing issues.