Is it possible to use Boost serialization as a header only library? - c++

Below is a minimal example to use the great Boost.Serialization library.
To compile the library I need to link with the boost_serialization precompiled library.
$ c++ -std=c++11 example.cpp -o example.x -lboost_serialization
^^^^^^^^^^^^^^^^^^^^^
The library is heavily templated an although complicated internally the actual code (function body) is quite simple. There are only a few references that need the linking, namely:
boost::archive::text_oarchive_impl<boost::archive::text_oarchive>::text_oarchive_impl(std::ostream&, unsigned int)
boost::archive::text_iarchive_impl<boost::archive::text_iarchive>::text_iarchive_impl(std::istream&, unsigned int)
boost::archive::text_iarchive_impl<boost::archive::text_oarchive>::~text_oarchive_impl()
boost::archive::text_iarchive_impl<boost::archive::text_iarchive>::~text_iarchive_impl()
...
boost::archive::archive_exception::~archive_exception()'
Is there a chance that the library can be used without linking as a header-only library?
For example some undocumented trick or hack?
That would make it more simple to use in some supercomputer clusters and environments where it is not that simply to compile Boost.
#include<sstream>
#include<numeric>
#include<boost/archive/text_oarchive.hpp> // needs linking
#include<boost/archive/text_iarchive.hpp>
#include<boost/serialization/vector.hpp>
int main(){
std::vector<double> v(10); std::iota(v.begin(), v.end(), 0);
std::stringstream ss;
{
boost::archive::text_oarchive toa(ss);
toa << v;
}
std::vector<double> v2;
boost::archive::text_iarchive tia(ss);
tia >> v2;
assert(v == v2);
}
EDIT: I would be very cool if the library gave the option to be header only, like Boost.Asio does (https://stackoverflow.com/a/40729439/225186.)
EDIT2: The author and maintainer of Boost.Serialization rejected the idea of making it header only. https://github.com/boostorg/serialization/issues/71

I ended up including the cpp sources from a certain version of Boost Serialization.
I chose the cpp files by trial and error.
https://gitlab.com/correaa/boost-mpi3/-/tree/master/include/mpi3/serialization_hack
Basically, I include these cpp files from the same place I would include the Serialization hpp files.
#include <boost/archive/detail/common_iarchive.hpp>
#include <boost/archive/detail/common_oarchive.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/basic_streambuf_locale_saver.hpp>
#include <boost/archive/detail/auto_link_archive.hpp>
//#include <boost/archive/detail/abi_prefix.hpp> // must be the last header
#include <boost/serialization/array.hpp>
#include <boost/serialization/is_bitwise_serializable.hpp>
#include <boost/serialization/item_version_type.hpp>
#include <boost/serialization/string.hpp>
#include <boost/mpl/placeholders.hpp>
#include <any>
#include <optional>
// use this to avoid need for linking -lserialization
#ifdef _MAKE_BOOST_SERIALIZATION_HEADER_ONLY
//#include <boost/archive/detail/decl.hpp>
#if BOOST_VERSION > 106000 && BOOST_VERSION < 106600
#include "../mpi3/serialization_hack/singleton.cpp"
#endif
#if BOOST_VERSION < 105900
#define BOOST_ARCHIVE_DECL
#define BOOST_SERIALIZATION_DECL
#endif
// NOLINTBEGIN(hicpp-use-auto,modernize-use-auto) external code
#include "../mpi3/serialization_hack/archive_exception.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/basic_archive.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/basic_iarchive.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/basic_iserializer.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/basic_oarchive.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/basic_oserializer.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/extended_type_info.cpp" // NOLINT(bugprone-suspicious-include) hack
#include "../mpi3/serialization_hack/extended_type_info_typeid.cpp" // NOLINT(bugprone-suspicious-include) hack
// NOLINTEND(hicpp-use-auto,modernize-use-auto)
A problem with this approach is that I had to modify the sources to accommodate different versions of Boost.Serialization and also had to do some modification to appease compiler warnings and static analyzers.

Related

How to define F_CPU for external libraries?

I want to write library for avr which uses <util/delay.h> and it requires F_CPU to be defined, so i faced the problem, is there any way in C/C++ for libraries to get defines for attributes which user writes in main file? I guess the question is kinda dumb, since libraries compiles first, then the main file. I've come to a solution with using init.h file, which contains user initial setups, still doesn't solve the problem with already compiled libraries. Still, maybe there are some other workarounds or standardized approach for this type of problem. here's the code:
main.cpp
#include "myLib.h"
#include <avr/io.h>
#include <util/delay.h>
int main(){
DDRD = 0xFF;
PORTD = 0;
while (1)
{
blink();
}
return 0;
}
init.h
#pragma once
#define F_CPU 16000000UL
myLib.h
#pragma once
#include "init.h"
#include <avr/io.h>
#include <util/delay.h>
void blink();
myLib.cpp
#include "myLib.h"
void blink(){
PORTD ^= 0xFF;
_delay_ms(500);
}
In your Makefile add -DF_CPU=16000000UL to the C_FLAGS.
Adding -DF_CPU to C_FLAGS is how this is commonly done.

Pre-Compiled Header (why need to include in every source file)

I am trying to understand pre-compiled headers. So I set up the following sample project:
pch.hpp
#include <vector>
pch.cpp
#pragma once
#include "pch.hpp"
Vector.hpp
#pragma once
#include "pch.h"
class Vector {
public:
Vector(const size_t N, const int init);
private:
std::vector<int> m_data;
};
Vector.cpp
#pragma once
#include "pch.hpp"
#include "Vector.hpp"
Vector::Vector(const size_t N, const int init)
: m_data(N, init) { }
Source.cpp
#include "pch.hpp"
#include "Vector.hpp"
int main() {
const Vector v1(3, 5);
}
I did this in VS 2022 by marking pch.hpp as the pre-compiled header file under Project Properties --> C++ --> Pre-Compiled Headers --> Header File with Use (/Yu). And setting pch.cpp as Create (/Yc).
Now, when I remove #include "pch.hpp" in Vector.cpp or Source.cpp, I get this error:
File Vector.cpp
Line 10
Severity Error
Code C1010
Description unexpected end of file while looking for precompiled header.
Did you forget to add '#include "pch.hpp"' to your source?
However, if I disable pre-compiled headers, everything compiles fine. As one would expect.
So, it seems that with pre-compiled headers enabled, I now need to put #include "pch.hpp" into every other source file? Even though #include "Vector.hpp" already includes pch.hpp? Is it possible to avoid having to put #include "pch.hpp" into all my other source files?
I ask, because I have a much larger code base (than the example above), and I thought I could essentially replace all instances of #include <vector> with #include "pch.hpp" and be done. But having to add #include "pch.hpp" to numerous cpp files, requires me to touch a lot of files (and seems counter-intuitive).
Apologies for the possibly naive question, as I am new to pre-compiled headers.

Boost serialization exception error

I have recently ran into an error when compiling my program with the boost serialization library.
I have read across many forums that I need to link the boost serialization library (-lboost_serialization). Which, I did.
However, there is one error that I am getting on the code:
/usr/local/boost_1_64_0/boost/serialization/throw_exception.hpp:36: undefined reference to `boost::archive::archive_exception::archive_exception(boost::archive::archive_exception const&)'
So I thought, what if I have to also include the exception library?
So I added this into the libraries for my compiler settings:
-lboost_exception
However, now it is saying that it is unable to find the boost_exception library. Which is interesting because I am able to see it in the file system!
I am wondering if I am doing the right thing or am I forgetting to add something to my compiler settings? Any help will be much appreciated!
Edit:
Here is the code:
// MS compatible compilers support #pragma once
#if defined(_MSC_VER)
# pragma once
#endif
// boost/throw_exception.hpp
//
// Copyright (c) 2002 Peter Dimov and Multi Media Ltd.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#ifndef BOOST_NO_EXCEPTIONS
#include <exception>
#endif
namespace boost {
namespace serialization {
#ifdef BOOST_NO_EXCEPTIONS
inline void throw_exception(std::exception const & e) {
::boost::throw_exception(e);
}
#else
template<class E> inline void throw_exception(E const & e){
throw e;// Error occurs here
}
#endif
} // namespace serialization
} // namespace boost
Here is a code sample where the breakage occurs
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <vector>
#include "boost/serialization/vector.hpp"
#include <string>
#include <fstream>
int main()
{
std::ofstream ofs("/home/phillip/test.txt");
std::vector<std::string> tests;
boost::archive::text_oarchive oa(ofs);
oa << tests;
}
I've seen this happen when linking to the boost library when compiling with a different compiler/version/flags.
E.g. many Boost Serialization answer programs wouldn't link when compiling with Clang on Coliru whereas they'd run fine when compiling with Gcc

Linking boost with own headers and source code

I'm working with the boost libraries and opencv, and now I have to implement my own headers and source code (additional to main.cpp) that requires the libraries as well.
The main.cpp looks like (just in principle):
// STL includes
#include <stdlib.h>
...(some other STL stuff)
// Boost includes
#include <boost/array.hpp>
...(a lot of other boost stuff)
//OpenCV
#include <opencv2/opencv.hpp>
// Own header files
#include <myHeader.hpp>
main() do_some_stuff;
This works, if I don't have anything related to boost in myStuff.hpp. But if I add something in it (the function descriptions are in myStuff.cpp), like:
class aClass{
public:
aClass(int);
void doSomething(boost::shared_ptr<int>);
void doSomethingElse(cv::Mat);
};
then it says 'boost' had not been declared, or 'cv' does not name a type.
I was like, ok, I just need to include the headers in this file as well, so I added the same includes, but then when it tries to link it gives a lot of errors like:
/usr/include/boost/operators.hpp:308:35: error: expected identifier before numeric constant
/usr/include/boost/operators.hpp:308:35: error: expected ‘>’ before numeric constant
/usr/include/boost/operators.hpp:310:1: error: expected class-name before ‘{’ token
/usr/include/boost/operators.hpp:311:3: error: expected unqualified-id before numeric constant
...(a lot more of these errors)
I'm using a Makefile to build this project, that looks like:
OPENCV_I = `pkg-config --cflags opencv`
#it finds boost without any additional -I or -L options...
INCLUDEPATHS = $(OPENCV_I)\
-I.\
-L.
LIBS=-lGL -lGLU -lm -lboost_program_options -lboost_system -lboost_thread -pthread -lrt -lopencv_core -lopencv_highgui
SRCCXX := main.cpp myStuff.cpp
OBJSCXX := $(SRCCXX:%.cpp=${BUILDDIR}/%.o)
$(BUILDDIR)/%.o : %.cpp
$(CXX) $(CXXFLAGS) $(INCLUDEPATHS) -c $< -o $# -DdDOUBLE $(LIBS)
all: ${OBJSCXX}
$(CXX) $(LDFLAGS) $(INCLUDEPATHS) -o $(OUTNAME) $? -DdDOUBLE $(LIBS)
Previously I was using CMake, and it worked quite well with these kind of projects, just this one is a part of a bigger project where they use Makefiles for everything. So I guess the main problem is with the makefile, probably when I list my source codes SRCCXX := main.cpp Visualisation.cpp it doesn't like it...
Any suggestions?
Thank you in advance.
EDIT....................................
So my whole myStuff.hpp looks like:
#define _USE_MATH_DEFINES
#include <math.h>
#define RINGS 5
#define SECTIONS 12
// Boost includes
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/assign/ptr_list_of.hpp>
#include <boost/assign.hpp>
#include <boost/bind.hpp>
#include <boost/date_time.hpp>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/make_shared.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/program_options.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <opencv2/opencv.hpp>
class Sensor{
public:
Sensor(int);
void Update(boost::shared_ptr<char[RINGS][SECTIONS]>);
int Id();
private:
char data[RINGS][SECTIONS];
int id;
};
and the myStuff.cpp:
#include "myStuff.hpp"
void Sensor::Update(boost::shared_ptr< char[RINGS][SECTIONS] > buffer){
for(int i=0;i<RINGS;i++) for(int j=0;j<SECTIONS;j++) data[i][j]=buffer[i][j];
};
Sensor::Sensor(int a){
id=0;
};
int Sensor::Id(){
return id;
};
and my main.cpp:
// STL includes
#include <stdlib.h>
#include <iostream>
#include <mutex>
#include <string.h>
#include <typeinfo>
#include <queue>
#include <memory>
// Boost includes
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/assign/ptr_list_of.hpp>
#include <boost/assign.hpp>
#include <boost/bind.hpp>
#include <boost/date_time.hpp>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/make_shared.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/program_options.hpp>
#include <boost/numeric/ublas/matrix.hpp>
//OpenCV
#include <opencv2/opencv.hpp>
// Own header files
#include "myStuff.hpp"
////////////////////////////////////////// Main
int main (int argc, char **argv)
{
Sensor sensor(0);
return 0;
}
Well first things first... when including your own headers from within your project I would recommend that you use "mine.hpp" instead of <mine.hpp>. This ensures that the compiler won't search the complete include path and accidentally find some other include of the same name (different version for example).
Second, when you have a dependency within a class then you include the header for that dependency within that class. You cannot make assumptions that someone will include all your dependencies in a main class or some other class. Sometimes someone will just want to use your class by itself. You don't want them to then have to figure out your dependencies. Don't worry about replication either as the include guard (or pragma) will prevent that.
As for your particular problems you will need to give use your code. You have certainly managed to correctly include your headers at that point. I would guess that they might stem from a missing { or ; somewhere. Look at your very first error and solve that one.
EDIT
The issue appears to be with how you are using boost. I had a look at what in operators.hpp and in version 1_44 what I see is a struct definition with 4 template parameters, one of which is defaulted to boost::detail::empty_base<T>. Only thing I can say is to make sure that you have your entire boost library on your include path, and link path.
EDIT2
From your newly posted code I see a couple of problems. First is that you have WAY too many includes in your header. You should only ever have class dependency includes in your header, and always prefer to put your header files into your implementation (.cpp) file. This helps to prevent extremely long compilation times. So first modify your header to include only the dependencies you need:
#include <boost/shared_ptr.hpp>
#define RINGS 5
#define SECTIONS 12
class Sensor{
public:
Sensor(int);
void Update(boost::shared_ptr<char[RINGS][SECTIONS]>);
int Id();
private:
char data[RINGS][SECTIONS];
int id;
};
Then in your implementation the only change is to put braces around your for loops (this is for clarity and safety... understand why you put it on one line but it is not worth it). Also put your CTOR first:
#include "myStuff.hpp"
Sensor::Sensor(int a){
id=0;
};
void Sensor::Update(boost::shared_ptr< char[RINGS][SECTIONS] > buffer){
for(int i=0;i<RINGS;i++) {
for(int j=0;j<SECTIONS;j++) {
data[i][j]=buffer[i][j];
}
}
};
int Sensor::Id(){
return id;
};
Main
#include "myStuff.hpp"
int main (int argc, char **argv)
{
Sensor sensor(0);
return 0;
}
The ONLY dependency you need is boost::shared_ptr. To be honest though, I'm pretty sure you don't need that either. Anyway I would recommend that you start with the above and then build up by adding one dependency at a time.
After a couple of hours I figured it out. myStuff.cpp also requires the header inclusion. Silly mistake, but still why can't the compiler says something like non defined or can't find instead of a couple of pages of messed up errors...?
Thanks anyway for your help.

Why use precompiled headers (C/C++)?

Why use precompiled headers?
Reading the responses, I suspect what I've been doing with them is kind of stupid:
#pragma once
// Defines used for production versions
#ifndef PRODUCTION
#define eMsg(x) (x) // Show error messages
#define eAsciiMsg(x) (x)
#else
#define eMsg(x) (L"") // Don't show error messages
#define eAsciiMsg(x) ("")
#endif // PRODUCTION
#include "targetver.h"
#include "version.h"
// Enable "unsafe", but much faster string functions
#define _CRT_SECURE_NO_WARNINGS
#define _SCL_SECURE_NO_WARNINGS
// Standard includes
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <direct.h>
#include <cstring>
#ifdef _DEBUG
#include <cstdlib>
#endif
// Standard Template Library
#include <bitset>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>
#include <string>
#include <numeric>
// Boost libraries
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_array.hpp>
//Windows includes
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "FILETIME_Comparisons.h"
#include <shlwapi.h>
#include <Shellapi.h>
#include <psapi.h>
#include <imagehlp.h>
#include <mscat.h>
#include <Softpub.h>
#include <sfc.h>
#pragma comment(lib, "wintrust.lib")
#pragma comment(lib,"kernel32.lib")
#pragma comment(lib,"Psapi.lib")
#pragma comment(lib,"shlwapi.lib")
#pragma comment(lib,"imagehlp.lib")
#pragma comment(lib,"Advapi32.lib")
#pragma comment(lib,"Shell32.lib")
#pragma comment(lib,"Sfc.lib")
#pragma comment(lib,"Version.lib")
// Crypto ++ libraries
#ifdef _DEBUG
#pragma comment(lib,"cryptlibd.lib")
#else
#pragma comment(lib,"cryptlib.lib")
#endif
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <md5.h>
#include <sha.h>
// String libraries
#include "stringUnicodeConversions.h"
#include "expandEnvStrings.h"
#include "randomString.h"
#include "getShortPathName.h"
// Regular Expression Libraries
#include "fpattern.h"
// File Result Record
#include "unixTimeToFileTime.h"
#include "fileData.h"
// Writer
#include "writeFileData.h"
// Criteria Structure System
#include "priorities.h"
#include "criterion.H"
#include "OPSTRUCT.H"
#include "regexClass.H"
#include "FILTER.h"
// Sub Programs Root Class
#include "subProgramClass.h"
// Global data
#include "globalOptions.h"
// Logger
#include "logger.h"
// Console parser
#include "consoleParser.h"
// Timeout handler
#include "timeoutThread.h"
// Zip library
#include "zip.h"
#include "unzip.h"
#include "zipIt.h"
// Scanner
#include "mainScanner.h"
#include "filesScanner.h"
// Sub Programs
#include "volumeEnumerate.h"
#include "clsidCompressor.h"
#include "times.h"
#include "exec.h"
#include "uZip.h"
// 64 bit support
#include "disable64.h"
In C/C++, the #include mechanism is a textual copy of the file specified into the current file. Headers include other headers (which include yet other headers), so when you do a #include, it could be adding tens of thousands of lines of C++ into each cpp file (or cxx, c, whatever), all of which need to be compiled each time. This can be a severe bottleneck for large projects.
Precompiled headers speed this up by compiling each header once, then including that compiled state into the cpp they are included in.
It compiles a lot quicker. C++ compilation takes years without them. Try comparing some time in a large project!
Re: your current usage, if you have a target with a very large number of files, it may still be faster to use PCH in that way - try switching them off to find out. It depends: if you have a lot of headers of your own, and you change them only infrequently, and you have a very large number of source files that you change much more frequently, then your PCH usage will cut rebuild times.
But normal advice is to only put things in PCH that never change, because there is a certain overhead to producing the PCH itself. If you trigger that off with every rebuild (by constantly tweaking one of your headers), using PCH may make the rebuild slower.
So you don't have to compile them every time you build your project. They're used for system headers that aren't going to change.
It speeds up compilation.
When you're including headers from other projects, you don't expect to change them. If you put these into a precompiled header, then that code will not have to be recompiled when you make changes to your source code. This reduces repetitive compilation of unchanged code, speeding up compile time.