Comments (13)
I should mention I am on the head of "develop"
from cereal.
Minimal test included!
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include "cereal/cereal.hpp"
#include "cereal/types/map.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/memory.hpp"
#include "cereal/types/string.hpp"
#include "cereal/types/base_class.hpp"
#include "cereal/archives/json.hpp"
#include <cereal/types/polymorphic.hpp>
class BaseClass : public std::enable_shared_from_this<BaseClass> {
public:
virtual ~BaseClass(){}
template <class Archive>
void serialize(Archive & archive){
archive(CEREAL_NVP(name), CEREAL_NVP(baseMember));
}
protected:
BaseClass(const std::string &a_name):
name(a_name){
}
std::string name;
int baseMember; //let this have random junk so we can see if it saves right.
};
class DerivedClass : public BaseClass {
friend cereal::access;
public:
static std::shared_ptr<DerivedClass> make(const std::string &a_name, int a_derivedMember){
return std::shared_ptr<DerivedClass>(new DerivedClass(a_name, a_derivedMember));
}
template <class Archive>
void serialize(Archive & archive){
archive(CEREAL_NVP(derivedMember), cereal::make_nvp("base", cereal::base_class<BaseClass>(this)));
}
private:
DerivedClass(const std::string &a_name, int a_derivedMember):
BaseClass(a_name),
derivedMember(a_derivedMember){
}
template <class Archive>
static DerivedClass * load_and_allocate(Archive &archive){
int derivedMember;
archive(CEREAL_NVP(derivedMember));
DerivedClass* object = new DerivedClass("", derivedMember);
archive(cereal::make_nvp("base", cereal::base_class<BaseClass>(object)));
return object;
}
int derivedMember;
};
CEREAL_REGISTER_TYPE(DerivedClass);
void saveTest(){
std::stringstream stream;
{
cereal::JSONOutputArchive archive(stream);
auto testSave = DerivedClass::make("TestName", 4);
archive(cereal::make_nvp("test", testSave));
}
std::cout << stream.str() << std::endl;
std::shared_ptr<DerivedClass> loaded;
{
cereal::JSONInputArchive archive(stream);
archive(cereal::make_nvp("test", loaded));
}
std::stringstream stream2;
{
cereal::JSONOutputArchive archive(stream2);
archive(cereal::make_nvp("test", loaded));
}
std::cout << stream2.str() << std::endl;
std::cout << "TA-DA!" << std::endl;
}
int main(){
saveTest();
}
from cereal.
Stack Overflow question for points if you like: http://stackoverflow.com/questions/20940919/c11-cereal-load-and-allocate-not-loading-correctly
from cereal.
New minimal test! It seems like even the most basic example fails. I was looking for coverage in unit tests and I didn't see any instance of load_and_allocate being used.* (edit: it seems this stuff is in the sandbox cpp files, but in these examples there are no member values being loaded and so this case would not be hit.) I tried this with XML archives as well with the same problem (though instead the exception throws from xml.hpp startNode()).
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include "cereal/cereal.hpp"
#include "cereal/types/map.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/memory.hpp"
#include "cereal/types/string.hpp"
#include "cereal/types/base_class.hpp"
#include "cereal/archives/json.hpp"
class BasicClass {
friend cereal::access;
public:
static std::shared_ptr<BasicClass> make(const std::string &a_name, int a_member){
return std::shared_ptr<BasicClass>(new BasicClass(a_name, a_member));
}
template <class Archive>
void serialize(Archive & archive){
archive(CEREAL_NVP(name), CEREAL_NVP(member));
}
private:
BasicClass(const std::string &a_name, int a_member):
name(a_name),
member(a_member){
}
template <class Archive>
static BasicClass * load_and_allocate(Archive &archive){
std::string name;
int member;
archive(CEREAL_NVP(name), CEREAL_NVP(member));
BasicClass* object = new BasicClass(name, member);
return object;
}
std::string name;
int member;
};
void saveTest(){
std::stringstream stream;
{
cereal::JSONOutputArchive archive(stream);
auto testSave = BasicClass::make("TestName", 4);
archive(cereal::make_nvp("test", testSave));
}
std::cout << stream.str() << std::endl;
std::shared_ptr<BasicClass> loaded;
{
cereal::JSONInputArchive archive(stream);
archive(cereal::make_nvp("test", loaded));
}
std::stringstream stream2;
{
cereal::JSONOutputArchive archive(stream2);
archive(cereal::make_nvp("test", loaded));
}
std::cout << stream2.str() << std::endl;
std::cout << "TA-DA!" << std::endl;
}
int main(){
saveTest();
}
from cereal.
Definitely a bug on our part introduced with the out of order loading. Probably also exists for XML archives. The bug is exactly in the search function where you described, and the issue is that we have a few special wrappers we sometimes use that introduce additional nodes into the JSON/XML trees that are cereal-specific metadata and not really relevant to a user looking at the JSON/XML.
Toying around two ideas to fix this in my head right now. One is to adjust the search function or introduce some state to the archive such that it knows when it is inside of a wrapper. The other solution would be to split cereal metadata into an entirely different node than actual data saved in a JSON/XML archive.
So for your example (slightly changed to return a shared_ptr<BaseClass>
instead of DerivedClass
to force use of the polymorphic stuff, and also modified to output exactly the same pointer twice in a row (with names nvp1
and nvp2
):
{
"cereal_data": {
"nvp1": {
"derivedMember": 4,
"base": {
"name": "TestName",
"baseMember": 0
}
}
},
"cereal_metadata_do_not_edit":
{
"nvp1": {
"polymorphic_id": 2147483649,
"polymorphic_name": "DerivedClass",
"ptr_id": 2147483649
}
"nvp2": {
"polymorphic_id": 1,
"ptr_id": 1
}
}
}
I'll have to think this over. The second approach would appeal to some others (e.g. DrAWolf) who have been asking for the text based archives to be more readable, and this would solve that issue more or less. We would likely have some decreased performance though.
from cereal.
Yeah, my initial reaction was that we would have to dig the "data" part out of the archive and pass that through in our load_andor_allocate calls in line 143 and 166 in memory.hpp:
ptr.reset( detail::Load<T, Archive>::load_andor_allocate( ar ) );
But I'm not really familiar with how you would do that.
I can see the merit to breaking out the structure as well, but I suspect that's a riskier and bigger task.
from cereal.
I'm not sure there is actually any dependence on load_and_allocate
, I think the bug is just to do with the wrappers.
from cereal.
Yeah, I'm kind of thrashing for an explanation because I am just not very familiar with this code base and in fact am kind of new to enable_if and template metaprogramming stuff that you guys employ a lot of. It's a very nested code base so I could be way off.
I guess I just see the memory.hpp save methods for PtrWrapper explicitly save data in a "data" field, but never explicitly pull the "data" field out in the corresponding load methods.
from cereal.
For now, to work around the issue I added a line 144 in memory.hpp (as it appears on line 168 in the case of no load_and_allocate which means that there is a default constructor.)
ar( *ptr );
I will simply avoid using the load_and_allocate archive directly and will use my serialization functions. In my load_and_allocate method I will construct an object with "default" like information.
from cereal.
Hey, just as I saw the relation to my suggestions, you already mention my name.
BUT it's not what you infer.
I STRONGLY disagree with the split data approach you are pondering.
Duplication/duplicity is the root of all evil - as we all (should) know.
from cereal.
The optimal solution would be a loader that could identify/recognize the polymorphic type - by looking at the entries present/given in a JSON object.
from cereal.
Actually looking at this the issue is with load_and_allocate, which does not enter into the data node created by the ptr_wrapper. In regards to the possible splitting of the data in the text archives, this doesn't duplicate any data except for NVP names to index the information. One node would contain data the other metadata. There is no way for us to avoid including this metadata somewhere. I think the only two good solutions are to keep it as is (which I don't think is such a big deal), or the alternative path.
from cereal.
Marking as closed let me know if issue persists.
from cereal.
Related Issues (20)
- Embedded RapidJSON can conflict with another RapidJSON used by the client (ODR violation!)
- Compilation error with struct as smat_pointer
- How to deserialize std::vector with XMLInputArchive
- Macos Build error: cannot bind lvalue of type unsigned long long to value of unrelated type unsigned long HOT 1
- I encountered a segmentation fault when processing XML file data with version 1.3.2 of Cereal. Could this be a security vulnerability
- pkg-config file
- std::aligned_storage is deprecated in C++23 HOT 1
- GCC 13.2 build error: possible dangling reference to a temporary HOT 2
- About the use of CEREAL_CLASS_VERSION HOT 1
- types/tuple.hpp does not compile on MSVC2019
- copy constructor is implicitly deleted because 'OutputArchiveBase' has a user-declared move constructor HOT 1
- Issue with closing braces in JSON using std::stringstream HOT 1
- Any plans for a new version?
- Is there a way to register polymorphic types during program execution? HOT 3
- How do I remove excess layers HOT 2
- RapidJson causes C5054 on MSVC HOT 1
- JSONOutputArchive destructor is not really noexcept(true) despite it's declared noexcept(true)
- cereal unable to export xml comments
- Compilation Failures when indirectly linking with Lapack HOT 1
- Defining Version Number At Runtime
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cereal.