C++:如何按值将对象从 unique_ptr 传递到函数中?
C++: How to pass the object from a unique_ptr into a function by value?
我想使用 github page 中的 cereal
库将 xml
加载到对象中。
到目前为止一切都很好。
但在我的应用程序中,它有点复杂:xml
文件需要 loaded/filled 的对象必须通过多态指针访问。
因此,如果 a 使用原始指针,cereal
库将拒绝接受它,并要求使用智能指针。
但是,当我将智能指针指向通用加载 cereal
函数(即 void serialize( Archive & ar )
与 cereal::XMLInputArchive::operator()
相关)时,它会尝试加载到整个指针本身而不是指向对象。
这是一个 MWE:
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <map>
#include <memory>
#include <cereal/archives/xml.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/polymorphic.hpp>
using namespace std;
class SuperParentClass
{
public:
virtual std::string get_nameClass() =0;
virtual int run() = 0;
void serialize() ;
};
class ClassRectangle : public SuperParentClass
{
public:
std::string get_nameClass(){return "Rectangle";};
double length=0.;
double width=0.;
template <class Archive>
void serialize( Archive & ar )
{
ar( CEREAL_NVP( length ) );
ar( CEREAL_NVP( width ) );
}
int run()
{
std::cout << "I am a Rectangle with length "<<length<<" and width "<<width << std::endl;
return 0;
}
};
CEREAL_REGISTER_TYPE(ClassRectangle)
CEREAL_REGISTER_POLYMORPHIC_RELATION(SuperParentClass, ClassRectangle)
int main(void)
{
// Beginning of main.
cout << "(Start)" << endl;
// Loading from file Part.
{
cout << "Load " << endl;
std::ifstream is("input.xml");
cereal::XMLInputArchive arr(is);
ClassRectangle Rec;
arr( Rec ); // or // arr( cereal::make_nvp("Rectangle", Rec) );
Rec.run();
// Now, a bit more complex but closer to my application, because I need a polymorphic pointer !
std::unique_ptr<SuperParentClass> UPSPC(new ClassRectangle());
arr( cereal::make_nvp("Rectangle", UPSPC ) ); // ask for a polymorphic_id, it does not apply the right function
// arr( cereal::make_nvp("Rectangle", UPSPC.get() ) ); // exempt a smart pointer not a raw pointer
// arr( cereal::make_nvp("Rectangle", &UPSPC ) ); // exempt a smart pointer not a raw pointer
// arr( cereal::make_nvp("Rectangle", std::move(UPSPC) ) ); // ask for a polymorphic_id, it does not apply the right function
// arr( cereal::make_nvp("Rectangle", *UPSPC.get() ) ); // does not compile.
// arr( UPSPC ); // create an Segmentation Error, because cereal is looking for a polymorphic_id I guess.
UPSPC->run();
}
// End of the main.
cout << "(End) " << endl;
return 0;
}
// EoF
与 input.xml
:
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<Rectangle>
<length>2</length>
<width>11</width>
</Rectangle>
<Rectangle>
<length>12</length>
<width>10</width>
</Rectangle>
</cereal>
和来自cereal.hpp
的make_nvp
的签名:
template <class T> inline
NameValuePair<T> make_nvp( std::string const & name, T && value )
{
return {name.c_str(), std::forward<T>(value)};
}
上述MWE的确切错误信息是:
terminate called after throwing an instance of 'cereal::Exception'
what(): XML Parsing failed - provided NVP (polymorphic_id) not found
Abandon
所以我的问题是:如何通过值将对象从 unique_ptr
传递到 cereal
函数?
或者有其他方法可以解决这个问题吗?
问题是您加载文件的方式与编写文件的方式不同。 如果您使用智能指针编写它,则该文件包含其他数据,包括一个节点polymorphic_id
。您将拥有一个与此类似的文件:
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<value0>
<polymorphic_id>2147483649</polymorphic_id>
<polymorphic_name>Rectangle</polymorphic_name>
<ptr_wrapper>
<valid>1</valid>
<data>
<length>1</length>
<width>5</width>
</data>
</ptr_wrapper>
</value0>
<value1>
<polymorphic_id>1</polymorphic_id>
<ptr_wrapper>
<valid>1</valid>
<data>
<length>2</length>
<width>25</width>
</data>
</ptr_wrapper>
</value1>
</cereal>
修改注册后使用
CEREAL_REGISTER_TYPE_WITH_NAME(ClassRectangle, "Rectangle")
而不是:
CEREAL_REGISTER_TYPE(ClassRectangle)
我用来写文件的代码是:
std::ofstream is("output.xml");
cereal::XMLOutputArchive arr(is);
auto *rp1 = new ClassRectangle();
rp1->length = 1;
rp1->width = 5;
std::unique_ptr<SuperParentClass> p1(rp1);
auto *rp2 = new ClassRectangle();
rp2->length = 2;
rp2->width = 25;
std::unique_ptr<SuperParentClass> p2(rp2);
arr(p1);
arr(p2);
然后可以使用指向 SuperParentClass
的智能指针读回该文件。
或者,如果您想在智能指针中读取原始文件,可以使用以下几行轻松完成:
std::unique_ptr<ClassRectangle> UPSPC(new ClassRectangle());
arr(*UPSPC);
UPSPC->run();
显然,您必须在此处使用派生类型,因为保存文件时没有处理多态类型所需的层次信息。
还要注意表达式 arr(*UPSPC)
中的 *。这样,您将加载到一个已分配的对象中。
更新
如果每种派生类型最多有一个对象,则可以按名称加载它们:
std::unique_ptr<ClassTriangle> tri(new ClassTriangle());
std::unique_ptr<ClassRectangle> rect(new ClassRectangle());
arr(cereal::make_nvp("Triangle", *tri));
arr(cereal::make_nvp("Rectangle", *rect));
rect->run();
tri->run();
我想使用 github page 中的 cereal
库将 xml
加载到对象中。
到目前为止一切都很好。
但在我的应用程序中,它有点复杂:xml
文件需要 loaded/filled 的对象必须通过多态指针访问。
因此,如果 a 使用原始指针,cereal
库将拒绝接受它,并要求使用智能指针。
但是,当我将智能指针指向通用加载 cereal
函数(即 void serialize( Archive & ar )
与 cereal::XMLInputArchive::operator()
相关)时,它会尝试加载到整个指针本身而不是指向对象。
这是一个 MWE:
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <map>
#include <memory>
#include <cereal/archives/xml.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/polymorphic.hpp>
using namespace std;
class SuperParentClass
{
public:
virtual std::string get_nameClass() =0;
virtual int run() = 0;
void serialize() ;
};
class ClassRectangle : public SuperParentClass
{
public:
std::string get_nameClass(){return "Rectangle";};
double length=0.;
double width=0.;
template <class Archive>
void serialize( Archive & ar )
{
ar( CEREAL_NVP( length ) );
ar( CEREAL_NVP( width ) );
}
int run()
{
std::cout << "I am a Rectangle with length "<<length<<" and width "<<width << std::endl;
return 0;
}
};
CEREAL_REGISTER_TYPE(ClassRectangle)
CEREAL_REGISTER_POLYMORPHIC_RELATION(SuperParentClass, ClassRectangle)
int main(void)
{
// Beginning of main.
cout << "(Start)" << endl;
// Loading from file Part.
{
cout << "Load " << endl;
std::ifstream is("input.xml");
cereal::XMLInputArchive arr(is);
ClassRectangle Rec;
arr( Rec ); // or // arr( cereal::make_nvp("Rectangle", Rec) );
Rec.run();
// Now, a bit more complex but closer to my application, because I need a polymorphic pointer !
std::unique_ptr<SuperParentClass> UPSPC(new ClassRectangle());
arr( cereal::make_nvp("Rectangle", UPSPC ) ); // ask for a polymorphic_id, it does not apply the right function
// arr( cereal::make_nvp("Rectangle", UPSPC.get() ) ); // exempt a smart pointer not a raw pointer
// arr( cereal::make_nvp("Rectangle", &UPSPC ) ); // exempt a smart pointer not a raw pointer
// arr( cereal::make_nvp("Rectangle", std::move(UPSPC) ) ); // ask for a polymorphic_id, it does not apply the right function
// arr( cereal::make_nvp("Rectangle", *UPSPC.get() ) ); // does not compile.
// arr( UPSPC ); // create an Segmentation Error, because cereal is looking for a polymorphic_id I guess.
UPSPC->run();
}
// End of the main.
cout << "(End) " << endl;
return 0;
}
// EoF
与 input.xml
:
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<Rectangle>
<length>2</length>
<width>11</width>
</Rectangle>
<Rectangle>
<length>12</length>
<width>10</width>
</Rectangle>
</cereal>
和来自cereal.hpp
的make_nvp
的签名:
template <class T> inline
NameValuePair<T> make_nvp( std::string const & name, T && value )
{
return {name.c_str(), std::forward<T>(value)};
}
上述MWE的确切错误信息是:
terminate called after throwing an instance of 'cereal::Exception'
what(): XML Parsing failed - provided NVP (polymorphic_id) not found
Abandon
所以我的问题是:如何通过值将对象从 unique_ptr
传递到 cereal
函数?
或者有其他方法可以解决这个问题吗?
问题是您加载文件的方式与编写文件的方式不同。 如果您使用智能指针编写它,则该文件包含其他数据,包括一个节点polymorphic_id
。您将拥有一个与此类似的文件:
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<value0>
<polymorphic_id>2147483649</polymorphic_id>
<polymorphic_name>Rectangle</polymorphic_name>
<ptr_wrapper>
<valid>1</valid>
<data>
<length>1</length>
<width>5</width>
</data>
</ptr_wrapper>
</value0>
<value1>
<polymorphic_id>1</polymorphic_id>
<ptr_wrapper>
<valid>1</valid>
<data>
<length>2</length>
<width>25</width>
</data>
</ptr_wrapper>
</value1>
</cereal>
修改注册后使用
CEREAL_REGISTER_TYPE_WITH_NAME(ClassRectangle, "Rectangle")
而不是:
CEREAL_REGISTER_TYPE(ClassRectangle)
我用来写文件的代码是:
std::ofstream is("output.xml");
cereal::XMLOutputArchive arr(is);
auto *rp1 = new ClassRectangle();
rp1->length = 1;
rp1->width = 5;
std::unique_ptr<SuperParentClass> p1(rp1);
auto *rp2 = new ClassRectangle();
rp2->length = 2;
rp2->width = 25;
std::unique_ptr<SuperParentClass> p2(rp2);
arr(p1);
arr(p2);
然后可以使用指向 SuperParentClass
的智能指针读回该文件。
或者,如果您想在智能指针中读取原始文件,可以使用以下几行轻松完成:
std::unique_ptr<ClassRectangle> UPSPC(new ClassRectangle());
arr(*UPSPC);
UPSPC->run();
显然,您必须在此处使用派生类型,因为保存文件时没有处理多态类型所需的层次信息。
还要注意表达式 arr(*UPSPC)
中的 *。这样,您将加载到一个已分配的对象中。
更新
如果每种派生类型最多有一个对象,则可以按名称加载它们:
std::unique_ptr<ClassTriangle> tri(new ClassTriangle());
std::unique_ptr<ClassRectangle> rect(new ClassRectangle());
arr(cereal::make_nvp("Triangle", *tri));
arr(cereal::make_nvp("Rectangle", *rect));
rect->run();
tri->run();