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.
I have created a fix.
Added Tests for this case:
https://github.com/Loki-Astari/ThorsSerializer/blob/master/src/Serialize/test/PolyMorphicSerializerUniquePointer.cpp
I have submitted this build to homebrew:
Homebrew/homebrew-core#37207
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.