I am having troubles to use cereal with the PIMPL idiom.
This is a minimal example:
b.h
#ifndef _B_H_
#define _B_H_
#include <memory>
#include "cereal/types/memory.hpp"
#include "cereal/archives/json.hpp"
struct BImpl;
class B
{
public:
B();
~B();
private:
std::unique_ptr<BImpl> _impl;
friend class cereal::access;
template <class Archive>
void serialize( Archive& ar )
{
ar( CEREAL_NVP( _impl ) );
}
};
#endif
b.cpp
#include "b.h"
struct BImpl
{
int b_i = 0;
private:
friend class cereal::access;
template <class Archive>
void serialize( Archive & ar )
{
ar(
CEREAL_NVP( b_i )
);
}
};
B::B() : _impl( new BImpl )
{
}
B::~B()
{
}
main.cpp
#include "b.h"
#include <fstream>
#include "cereal/archives/json.hpp"
using namespace std;
int main( int argc, char** argv )
{
B b1;
{
std::ofstream file( "out.json" );
cereal::JSONOutputArchive archive( file );
archive( CEREAL_NVP( b1 ) );
}
}
And here the errors that I get on MSVC 2015 Community Edition when I try to compile the minimal example:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\type_traits(428): error C2139: 'BImpl': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_polymorphic'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\type_traits(435): error C2139: 'BImpl': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_abstract'
I am quite sure that I am not the first attempting to do this, but I have not been able to find nothing specific in the documentation or code snippets with a working solution.
I have found a working solution following the approach described here: http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/pimpl.html
In practice, I have:
* moved the definition of B::serialize to B.cpp
* added in B.cpp all the different instantiations for the archives that I use
Here the ticket that describe the issue: https://github.com/USCiLab/cereal/issues/324
Related
I know it was discussed in length many times already, but my case has some particular condition I don't know how to address properly.
Library with /MDd solution builds OK. But it is not what the application needs as it requires /MT(d) version.
Now, I've changed the compiler options to /MTd, resolved some of the external projects dependencies, but still getting this:
Error LNK2019 unresolved external symbol "bool __cdecl std::uncaught_exception(void)" (?uncaught_exception#std##YA_NXZ) referenced in function "public: __cdecl std::basic_ostream<char,struct std::char_traits<char> >::sentry::~sentry(void)" (??1sentry#?$basic_ostream#DU?$char_traits#D#std###std##QEAA#XZ) vcruntime140 ..\vcruntime140 ..\vcruntime140\log.obj
log.h is just this:
#ifndef LOG_H
#define LOG_H
#include <string>
namespace hooks {
/** Prints message to the file only if debug mode setting is enabled. */
void logDebug(const std::string& logFile, const std::string& message);
/** Prints message to the file. */
void logError(const std::string& logFile, const std::string& message);
} // namespace hooks
#endif // LOG_H
log.cpp
#include "log.h"
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
namespace hooks {
static void logAction(const std::string& logFile, const std::string& message)
{
using namespace std::chrono;
std::ofstream file(logFile.c_str(), std::ios_base::app);
const std::time_t time{std::time(nullptr)};
const std::tm tm = *std::localtime(&time);
file << "[" << std::put_time(&tm, "%c") << "] " << message << "\n";
}
void logDebug(const std::string& logFile, const std::string& message)
{
logAction(logFile, message);
}
void logError(const std::string& logFile, const std::string& message)
{
logAction(logFile, message);
}
} // namespace hooks
hooks.h
#ifndef HOOKS_H
#define HOOKS_H
#include <string>
#include <utility>
#include <vector>
namespace hooks {
using HookInfo = std::pair<void**, void*>;
using Hooks = std::vector<HookInfo>;
/** Returns array of hooks to setup. */
Hooks getHooks();
Hooks getVftableHooks();
} // namespace hooks
#endif // HOOKS_H
hooks.cpp
#include "hooks.h"
namespace hooks {
Hooks getHooks()
{
Hooks hooks;
return hooks;
}
Hooks getVftableHooks()
{
Hooks hooks;
return hooks;
}
} // namespace hooks
Any idea of how to resolve it?
Found solution, the only remaining issue is actually unrelated to the above. The above errors are resolved in the following way:
Adding MSVCPRTD.LIB to the additional libraries link seem to elliminate the problems with LINK2019.
The problem was happening since some of the standard library functions defined in that lib were not loaded. I've realised it by checking the error message and googling of which function definitions are missing and which library they belong to.
But now there is a problem with MSVCPRTD.LIB as it is a dynamic and not a static library according to Microsoft docs: https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160
The problem with MSVCPRTD.LIB can be addressed by loading libcmtd.lib instead, which is the /MTd version of the same librari(es).
For an exercise i need to build a vector of polymorphic objects and for some reason both shared and Unique ptr make linkage errors 2019 and 1120 when i use them. i have no option to use the old way of memory allocation with New and Delete so i have to make it work. i tried various of different syntax options for doing this and still with no luck.
*note:
we use Cmake to bind the project together in visual studio
and also we splitting the objects into header and cpp files.
here are my objects for now:
//This is the abstract base class
#pragma once
#include <string>
class Shape
{
public:
Shape() = default;
virtual ~Shape() = default;
virtual std::string get_name() = 0;
private:
};
These are the derived classes:
#pragma once
#include "Shape.h"
class Circle : public Shape
{
public:
Circle();
~Circle();
virtual std::string get_name() override;
private:
std::string m_name;
};
Cpp file:
#pragma once
#include "Circle.h"
Circle::Circle()
: m_name("Circle")
{
}
Circle::~Circle()
{
}
std::string Circle::get_name() override
{
return m_name;
}
another derived class:
#pragma once
#include "Shape.h"
class Rectangle : public Shape
{
public:
Rectangle();
~Rectangle();
virtual std::string get_name() override;
private:
std::string m_name;
};
Cpp file:
#pragma once
#include "Rectangle.h"
Rectangle::Rectangle()
: m_name ("Rectangle")
{
}
Rectangle::~Rectangle()
{
}
std::string Rectangle::get_name() override
{
return m_name;
}
this is the class that operates the program:
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "Shape.h"
#include "Circle.h"
#include "Rectangle.h"
class Board
{
public:
Board();
~Board();
void run();
void objects_allocation();
private:
std::string m_string;
std::vector<std::shared_ptr<Shape>> m_gates;
};
Cpp file:
#pragma once
#include "Board.h"
Board::Board()
: m_string(" ")
{
}
Board::~Board()
{
}
void run()
{
while (m_string != "exit")
{
std::cin >> m_string;
}
std::cout << "Goodbye!" << std::endl;
}
void Board::Objects_allocation()
{
m_gates.push_back(std::make_shared <Circle>());
m_gates.push_back(std::make_shared <Rectangle>());
}
and here is my main function:
#pragma once
#include "Board.h"
int main()
{
Board board1;
board1.run();
return 0;
}
sincerely thank you if you could explain to me what went wrong here..
The Problem was in the Cmake file. now everything is working just like i wanted.
Platform specifics:
Fedora 21
g++ 4.9.2-6
Here's the error ....
$ g++ -c kabi-serial.cpp -lboost_serialization
kabi-serial.cpp: In function ‘void boost::serialization::kb_write_list()’:
kabi-serial.cpp:41:13: error: expected ‘;’ before ‘oa’
serialize oa(ofs);
kabi-serial.cpp:41:20: error: statement cannot resolve address of overloaded function
serialize oa(ofs);
^
kabi-serial.cpp:42:3: error: ‘oa’ was not declared in this scope
oa << ql;
And here's the kabi-serial.cpp source ...
#include <boost/serialization/vector.hpp>
#include <vector>
namespace boost {
namespace serialization {
class Cqnodelist
{
public:
friend class boost::serialization::access;
Cqnodelist(){}
std::vector<int>qnodelist;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & qnodelist;
}
};
void kb_write_list ()
{
Cqnodelist ql;
ofstream ofs("kabi-list.dat");
{
serialize oa(ofs);
oa << ql;
}
}
}
}
I based the source code on the simplest of the serialization examples, except using the serialization of the stl vector object.
I'm sure it's simple, but what am I missing?
To use ofstream you need to include <fstream>.
ofstream is in the namespace std: std::ofstream.
You don't need to put your code inside boost::serialization namespace (except case when you implement external to class serialize() function).
Adding serialize method implements way of loading and saving object attributes.
To actually store or load data you need to select storage — archive in Boost.Serialization terminology.
#include <boost/serialization/vector.hpp>
// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <vector>
#include <fstream> // required for std::ofstream
class Cqnodelist
{
public:
friend class boost::serialization::access;
Cqnodelist(){}
std::vector<int>qnodelist;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & qnodelist;
}
};
void kb_write_list ()
{
Cqnodelist ql;
// fill object with data
ql.qnodelist.push_back(1);
ql.qnodelist.push_back(2);
ql.qnodelist.push_back(3);
// See http://www.boost.org/doc/libs/1_57_0/libs/serialization/doc/tutorial.html
// for reference
// save data to archive
{
// open file that will contain serialized data
std::ofstream ofs("kabi-list.dat");
// create archive on top of opened file
boost::archive::text_oarchive oa(ofs);
// write class instance to archive
oa << ql;
}
// to load object later open file, create input archive on top of opened
// file and load object state
{
Cqnodelist new_ql;
// create and open an archive for input
std::ifstream ifs("kabi-list.dat");
boost::archive::text_iarchive ia(ifs);
// read class state from archive
ia >> new_ql;
}
}
int main()
{
kb_write_list();
}
I am trying to create Google Mock object for some interface class which uses boost::variant
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <boost/variant.hpp>
#include <vector>
typedef std::vector<int> VectorOfInt;
typedef boost::variant<VectorOfInt> VariantOfVector;
class InterfaceClass
{
public:
virtual ~InterfaceClass() {}
virtual void SetSome( const VariantOfVector& ) = 0;
virtual const VariantOfVector& GetSome() const = 0;
};
class MockInterfaceClass
{
public:
MOCK_METHOD1( SetSome, void( const VariantOfVector& ) );
MOCK_CONST_METHOD0( GetSome, const VariantOfVector&() );
};
When I compile it with
g++ mytest.cpp -o mytest
i get
/usr/include/boost/variant/detail/variant_io.hpp:64: error: no match for ‘operator<<’ in ‘((const boost::detail::variant::printer > >*)this)->boost::detail::variant::printer > >::out_ << operand’
Does boost::variant work with std::vector? It seems boost::variant works with any type I define but std:vector. Why?
Boost version - 1.45
g++ version - 4.4.5
It seems that the mock attempts to apply operator << to your variant. You have to define operator << for its contents, i.e. for std::vector template.
As Igor R. answered, you need to add operator << (without namespace) like this:
std::ostream& operator <<(std::ostream& out, VariantOfVector const& rhs)
{
//Print or apply your visitor to **rhs**
return out;
}
I am using boost::serialization in my project. The project is large, and serializes my objects in several places. According to the documentation here, I should export my class with two separated step.
BOOST_EXPORT_KEY() in .h file, witch contains the declaration.
BOOST_EXPOET_IMPLEMENT() in .cpp file, witch contains the instantiation(definition) of the exporting.
hier.h the class hierarchy, there are 3 classes in the hierarchy.
/*
B <---+--- D1
|
+--- D2
*/
#include <boost/serialization/base_object.hpp>
class B {
public:
virtual ~B() {}
template < typename Ar >
void serialize(Ar& ar, const int) {
}
} ;
class D1 : public B {
public:
virtual ~D1() {}
template < typename Ar > void serialize(Ar& ar, const int) {
boost::serialization::base_object<B>(*this);
}
} ;
class D2 : public B {
public:
template < typename Ar > void serialize(Ar& ar, const int) {
boost::serialization::base_object<B>(*this);
}
virtual ~D2() {}
} ;
#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT_KEY(B);
BOOST_CLASS_EXPORT_KEY(D1);
BOOST_CLASS_EXPORT_KEY(D2);
And a hier.cpp contains the implementation:
#include <boost/serialization/export.hpp>
#include "hier.h"
BOOST_CLASS_EXPORT_IMPLEMENT(D1);
BOOST_CLASS_EXPORT_IMPLEMENT(D2);
And a main.cpp use the serialization:
#include <iostream>
#include <sstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include "hier.h"
int main(int argc, char* argv[])
{
B* d1 = new D1();
B* d2 = new D2();
std::ostringstream os;
boost::archive::text_oarchive oa (os);
oa & d1 & d2;
}
It compiled without any problem, but run it will cause:
terminate called after throwing an instance of 'boost::archive::archive_exception'
what(): unregistered class - derived class not registered or exported
Which means the derived class is not registered, means the registration in the hier.cpp is not working. But that is really strange, because:
If I register implementation is both main.cpp and hier.cpp, it issue duplicated definition while linking. Means the registration in hier.cpp is OK and is exposed into the linkers visibility., otherwise there will be no duplicated definition error.
If I register implementation only in main.cpp, it runs OK.
I am really confused in that situation. Any comment and suggestion is appreciated. Thanks in advance.
Before calling BOOST_CLASS_EXPORT_* you should include the archives which you want to use. The maсro then adds specific serialize-functions for the headers.
This means you should change your code in hier.cpp to the following:
#include <boost/serialization/export.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include "hier.h"
BOOST_CLASS_EXPORT_IMPLEMENT(D1);
BOOST_CLASS_EXPORT_IMPLEMENT(D2);
The code in hier.h changes accordingly:
#include <boost/serialization/export.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
BOOST_CLASS_EXPORT_KEY(B);
BOOST_CLASS_EXPORT_KEY(D1);
BOOST_CLASS_EXPORT_KEY(D2);
Sources:
Boost Serialization Documentation
PS:
I do not know if this is solving your problem, but I think it could be causing some trouble. I think it's worth a try.