Skip intermediate classes in hierarchy inheritance with boost::serialization - c++

Context: I have a tree-like structure representing a AST of Expr that I want to serialize using boost::serialization. The main issue is that all classes have non default constructors and const children. To overcome this issue, I followed the doc and overloaded load_construct_data and save_construct_data (which end up doing all the work).
My question is about the Mul class within the code. To factorize code, I developed a template class Op2 that is used to define operators such as Add or Mul (only Mul is shown here) through CRTP on these classes. In Mul::serialize, I directly register Expr as base class of Muland completely skip Op2. The code works, valgrind is happy, but is it correct ? Or does boost::serialization require to ave the complete class hierarchy ?
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
//forward declaration of my structs
struct Expr;
struct Mul;
struct Int;
//forward declarations of custom boost functions to friend them in the class
namespace b_ser = boost::serialization;
namespace boost {
namespace serialization {
template <class Archive>
void load_construct_data(Archive &ar, Mul *e, const unsigned int);
template <class Archive>
void save_construct_data(Archive &ar, const Mul *a, const unsigned int);
template <class Archive>
void load_construct_data(Archive &ar, Int *e, const unsigned int);
template <class Archive>
void save_construct_data(Archive &ar, const Int *a, const unsigned int);
} // namespace serialization
} // namespace boost
//memory manager
std::vector<std::unique_ptr<Expr>> pool;
// AST
struct Expr {
virtual ~Expr() {}
virtual std::vector<Expr const *> children() const = 0;
virtual std::string identity() const = 0;
void print(int p) const {
std::cout << std::setw(p) << ' ';
std::cout << identity() << "\n";
for (auto a_kid : children()) {
a_kid->print(p + 2);
}
}
void self_register() const {
if (std::find_if(pool.begin(), pool.end(), [this](auto const &stored_ptr) {
return this == stored_ptr.get();
}) == pool.end()) {
pool.push_back(std::unique_ptr<Expr>(const_cast<Expr *>(this)));
}
for (auto ptr : children()) {
ptr->self_register();
}
}
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {}
};
struct Int : Expr {
int const n;
std::vector<Expr const *> children() const override { return {}; }
std::string identity() const override {
return "Int[" + std::to_string(n) + "]#";
}
Int(int nn) : n(nn) {}
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &boost::serialization::base_object<Expr>(*this);
}
template <class Archive>
friend void b_ser::save_construct_data(Archive &ar, const Int *i,
const unsigned int) {
ar << i->n;
}
template <class Archive>
friend void b_ser::load_construct_data(Archive &ar, Int *i,
const unsigned int) {
int n;
ar >> n;
::new (i) Int(n);
}
};
template <class T> struct Op2 : Expr {
std::vector<Expr const *> children() const override { return {l, r}; }
std::string identity() const override { return T::message; }
Op2(Expr const *ll, Expr const *rr) : l(ll), r(rr) {}
protected:
Expr const *l;
Expr const *r;
};
struct Mul : Op2<Mul> {
using Op2::Op2;
static auto const constexpr message = "Mul";
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &boost::serialization::base_object<Expr>(*this);
}
template <class Archive>
friend void b_ser::save_construct_data(Archive &ar, const Mul *a,
const unsigned int) {
ar << a->l;
ar << a->r;
}
template <class Archive>
friend void b_ser::load_construct_data(Archive &ar, Mul *e,
const unsigned int) {
Expr *l, *r;
ar >> l;
ar >> r;
::new (e) Mul(l, r);
e->self_register();
}
};
template <class T, class... Args> T *store(Args... args) {
auto to_store = std::make_unique<T>(std::forward<Args>(args)...);
auto raw_ptr = to_store.get();
pool.push_back(std::move(to_store));
return raw_ptr;
}
BOOST_CLASS_EXPORT(Expr)
BOOST_CLASS_EXPORT(Int)
BOOST_CLASS_EXPORT(Mul)
int main(int argc, char *argv[]) {
{
auto deux = store<Int>(2);
auto trois = store<Int>(3);
auto m_23 = store<Mul>(trois, deux);
auto quatre = store<Int>(4);
auto root = store<Mul>(m_23, quatre);
Expr *e_root = root;
root->print(2);
std::ofstream of("arxiv");
boost::archive::text_oarchive oa(of);
oa << e_root;
}
std::cout << "==================="
<< "\n";
{
std::ifstream isf("arxiv");
boost::archive::text_iarchive is(isf);
Expr *expr;
is >> expr;
expr->print(2);
}
return 0;
}

Firstly, I conclude you must be using MSVC since your code is not valid standard C++.
Fixing things shows it appears to work properly on Clang and GCC.
Enabling AddressSanitizer quickly reveals errors that I /believe/ might originate in the singleton code deep inside Boost Serialization. I'll ignore it for now.
Due to those errors, I looked at this a long time, trying to see whether the code was to blame.
While doing so, I found that many things could be made a lot simpler.
you can do without construct-data if you just add private default constructors for serialization purposes.
The important thing about your classes are the invariants, and it's easy to prove that invariants are kept across deserialization this way.
you can do without the subclasses for each binary operator, because they add literally no behaviour. Consider providing the message constant out-of-band
instead of making a vector<unique_ptr> you can use a pointer container that implicitly owns its elements. This makes e.g. looking up equivalent pointers a lot easier:
namespace memory_management {
struct address_less {
bool operator()(Expr const& a, Expr const& b) const { return &a < &b; }
};
static boost::ptr_set<Expr, address_less> pool;
}
void Expr::self_register() { memory_management::pool.insert(this); }
All in all things are much shorter:
// AST
struct Expr {
virtual ~Expr() = default;
virtual std::vector<Expr const *> children() const { return {}; }
virtual std::string identity() const = 0;
void print(int p) const {
std::cout << std::setw(p) << ' ';
std::cout << identity() << "\n";
for (auto a_kid : children()) {
a_kid->print(p + 2);
}
}
protected:
Expr() { self_register(); }
private:
void self_register();
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &, unsigned) {}
};
namespace memory_management {
struct address_less {
bool operator()(Expr const& a, Expr const& b) const { return &a < &b; }
};
static boost::ptr_set<Expr, address_less> pool;
}
void ::Expr::self_register() { memory_management::pool.insert(this); }
struct Int : Expr {
std::string identity() const override { return "Int[" + std::to_string(n) + "]#"; }
Int(int nn) : n(nn) {}
private:
int const n = 0;
friend class boost::serialization::access;
Int() = default;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& const_cast<int&>(n);
}
};
namespace Tags {
struct Mul;
struct Div;
struct Plus;
struct Minus;
template <typename T> constexpr char const* const message = "Unknown";
template <> constexpr char const* const message<Mul> = "Mul";
template <> constexpr char const* const message<Div> = "Div";
template <> constexpr char const* const message<Plus> = "Plus";
template <> constexpr char const* const message<Minus> = "Minus";
}
template <class T> struct Op2 : Expr {
std::vector<Expr const *> children() const override { return { l, r }; }
std::string identity() const override { return Tags::message<T>; }
Op2(Expr *ll, Expr *rr) : l(ll), r(rr) {}
protected:
friend class boost::serialization::access;
Op2() = default;
Expr *l = nullptr;
Expr *r = nullptr;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& l & r;
}
};
using Mul = Op2<Tags::Mul>;
using Div = Op2<Tags::Div>;
using Plus = Op2<Tags::Plus>;
using Minus = Op2<Tags::Minus>;
Bonus: A DSL For AST Building
I thought it would be nicer to be able to say:
Expr const* root((as_expr(3) * 2) + 5 + (as_expr(7) / 25));
So, let's do that:
namespace builder {
struct Atom {
Atom(Expr* expr) : expr(expr) {}
Atom(int i) : expr(new Int(i)) {}
Expr* expr;
explicit operator Expr const*() const { return expr; }
};
template <typename T>
Atom as_expr(T&& v) { return std::forward<T>(v); }
Atom operator+(Atom a, Atom b) { return new Plus(a.expr, b.expr); }
Atom operator-(Atom a, Atom b) { return new Minus(a.expr, b.expr); }
Atom operator*(Atom a, Atom b) { return new Mul(a.expr, b.expr); }
Atom operator/(Atom a, Atom b) { return new Div(a.expr, b.expr); }
}
LIVE DEMO
Live On Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/ptr_container/ptr_set.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
// AST
struct Expr {
virtual ~Expr() = default;
virtual std::vector<Expr const *> children() const { return {}; }
virtual std::string identity() const = 0;
void print(int p) const {
std::cout << std::setw(p) << ' ';
std::cout << identity() << "\n";
for (auto a_kid : children()) {
a_kid->print(p + 2);
}
}
protected:
Expr() { self_register(); }
private:
void self_register();
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &, unsigned) {}
};
namespace memory_management {
struct address_less {
bool operator()(Expr const& a, Expr const& b) const { return &a < &b; }
};
static boost::ptr_set<Expr, address_less> pool;
}
void ::Expr::self_register() { memory_management::pool.insert(this); }
struct Int : Expr {
std::string identity() const override { return "Int[" + std::to_string(n) + "]#"; }
Int(int nn) : n(nn) {}
private:
int const n = 0;
friend class boost::serialization::access;
Int() = default;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& const_cast<int&>(n);
}
};
namespace Tags {
struct Mul;
struct Div;
struct Plus;
struct Minus;
template <typename T> constexpr char const* const message = "Unknown";
template <> constexpr char const* const message<Mul> = "Mul";
template <> constexpr char const* const message<Div> = "Div";
template <> constexpr char const* const message<Plus> = "Plus";
template <> constexpr char const* const message<Minus> = "Minus";
}
template <class T> struct Op2 : Expr {
std::vector<Expr const *> children() const override { return { l, r }; }
std::string identity() const override { return Tags::message<T>; }
Op2(Expr *ll, Expr *rr) : l(ll), r(rr) {}
protected:
friend class boost::serialization::access;
Op2() = default;
Expr *l = nullptr;
Expr *r = nullptr;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& l & r;
}
};
using Mul = Op2<Tags::Mul>;
using Div = Op2<Tags::Div>;
using Plus = Op2<Tags::Plus>;
using Minus = Op2<Tags::Minus>;
namespace builder {
struct Atom {
Atom(Expr* expr) :expr(expr){}
Atom(int i) :expr(new Int(i)){}
Expr* expr;
explicit operator Expr const*() const { return expr; }
};
template <typename T>
Atom as_expr(T&& v) { return std::forward<T>(v); }
Atom operator+(Atom a, Atom b) { return new Plus(a.expr, b.expr); }
Atom operator-(Atom a, Atom b) { return new Minus(a.expr, b.expr); }
Atom operator*(Atom a, Atom b) { return new Mul(a.expr, b.expr); }
Atom operator/(Atom a, Atom b) { return new Div(a.expr, b.expr); }
}
BOOST_CLASS_EXPORT(Expr)
BOOST_CLASS_EXPORT(Int)
BOOST_CLASS_EXPORT(Mul)
BOOST_CLASS_EXPORT(Div)
BOOST_CLASS_EXPORT(Plus)
BOOST_CLASS_EXPORT(Minus)
int main() {
std::cout << std::unitbuf;
{
using builder::as_expr;
Expr const* root((as_expr(3) * 2) + 5 + (as_expr(7) / 25));
root->print(2);
std::ofstream of("arxiv");
boost::archive::text_oarchive oa(of);
oa << root;
}
std::cout << "===================\n";
{
std::ifstream isf("arxiv");
boost::archive::text_iarchive is(isf);
Expr *expr = nullptr;
is >> expr;
expr->print(2);
}
memory_management::pool.clear(); // no memory leaks
}
Prints
Plus
Plus
Mul
Int[3]#
Int[2]#
Int[5]#
Div
Int[7]#
Int[25]#
===================
Plus
Plus
Mul
Int[3]#
Int[2]#
Int[5]#
Div
Int[7]#
Int[25]#

Related

Call static function of specialize template with base class of type T

I need to write Stream class with a template Write function to accept any type and write it to stream.
I write a Stream class and a StreamWriter to partially specialize Write function, but compiler can't find static function of StreamWriter with base class of AInt class.
template<typename T>
class AType {
public:
T rawValue;
void Add(T v) { rawValue += v; }
void Sub(T v) { rawValue += v; }
void Mul(T v) { rawValue *= v; }
void Mod(T v) { rawValue %= v; }
};
class AInt : public AType<int> {
public:
using AType<int>::Add;
using AType<int>::Sub;
using AType<int>::Mul;
using AType<int>::Mod;
};
class AFloat : public AType<float> {
public:
using AType<float>::Add;
using AType<float>::Sub;
using AType<float>::Mul;
};
class AStream;
template<typename T>
class AStreamWriter {
public:
static bool Write(AStream *stream, T v);
};
class AStream {
public:
template<typename T>
bool Write(T v) {
return AStreamWriter<T>::Write(this, v);
}
};
template<typename T>
class AStreamWriter<AType<T>> {
public:
static bool Write(AStream *stream, AType<T> v) {
//Do somethings
return true;
}
};
int main() {
AInt x{10};
AStream stream;
stream.Write(x); //Error, Compiler can't find AStreamWriter<AInt>::Write
}
Is there any way to fix this problem?
Your specialization AStreamWriter<AType<T>> is unrelated to AStreamWriter<AInt> (and you call AStreamWriter<AInt>::Write(AStream*, AInt), not AStreamWriter<AType<int>>::Write(AStream*, AType<int>)).
overload or simple template function might be a alternative...
template<typename T>
static bool Write(const AType<T>&) { return true; }
I solve the problem with c++ concept.
IsAType is a concept that accepts all subclasses of AType.
template<typename T>
class AType {
public:
using RawType = T;
T rawValue;
void Add(T v) { rawValue += v; }
void Sub(T v) { rawValue += v; }
void Mul(T v) { rawValue *= v; }
void Mod(T v) { rawValue %= v; }
};
template<typename T>
concept IsAType = std::is_base_of_v<AType<typename T::RawType>, T>;
class AInt : public AType<int> {
public:
using AType<int>::Add;
using AType<int>::Sub;
using AType<int>::Mul;
using AType<int>::Mod;
};
class AFloat : public AType<float> {
public:
using AType<float>::Add;
using AType<float>::Sub;
using AType<float>::Mul;
};
class AStream;
template<typename T>
class AStreamWriter {
public:
static bool Write(AStream *stream, T v);
};
class AStream {
public:
template<typename T>
bool Write(T v) {
return AStreamWriter<T>::Write(this, v);
}
};
template<IsAType T>
class AStreamWriter<T> {
public:
static bool Write(AStream *stream, T v) {
//Do somethings
return true;
}
};
int main() {
AInt x{10};
AStream stream;
stream.Write(x);
}

How to write a template class to wrap a primitive type or char* parameter

I'm trying to write a template class that will wrap a type that could be either a primitive (uint8_t up to float) or a char* string. If the class wraps a primitive type, setting its value to any primitive will do a straight cast, while setting a char* will to an ascii to primitive conversion (atoi, sprintf, etc). If it wraps a char*, the class should manage an internal char* buffer and convert primitive to char*.
If I limit my example to only uint32_t values, this is the template that would deal with any primitive type (with obvious error checking details omitted):
template<typename T>
class Measurement
{
public:
Measurement();
~Measurement();
void setU32Value(uint32_t value);
void setStringValue(const char* std);
uint32_t asU32() const;
const char* asString() const;
private:
T _value;
};
template<typename T>
void Measurement<T>::setU32Value(uint32_t value)
{
_value = (T)value;
}
template<typename T>
void Measurement<T>::setStringValue(const char* value)
{
_value = (T)::atoi(value);
}
template<typename T>
uint32_t Measurement<T>::asU32() const
{
return (uint32_t)_value;
}
template<typename T>
const char* Measurement<T>::asString() const
{
ostringstream oss;
oss << _value;
return oss.str();
}
But it won't work if I try to Measurement<char*> because it won't convert setU32Value wont' convert "value" to a string representation and the asU32() won't convert it back.
Can anyone suggest a way to make this work?
Thanks.
Your class definitely should not manually manage the lifetime of its string. Please see the Single-responsibility principle, The rule of three/five/zero and RAII. So the string storage type should be std::string and not char*.
C++20 concepts:
#include <string>
#include <concepts>
#include <type_traits>
template<typename T>
class Measurement
{
private:
T _value{};
public:
Measurement() = default;
template <std::integral U> requires std::integral<T>
void setIntValue(U value)
{
_value = static_cast<U>(value);
}
template <std::integral U> requires std::same_as<T, std::string>
void setIntValue(U value)
{
_value = std::to_string(value);
}
void setStringValue(const std::string& str) requires std::integral<T>
{
_value = static_cast<T>(std::stoll(str));
}
void setStringValue(std::string str) requires std::same_as<T, std::string>
{
_value = std::move(str);
}
template <std::integral U> requires std::integral<T>
U asInt() const
{
return static_cast<U>(_value);
}
template <std::integral U> requires std::same_as<T, std::string>
U asInt() const
{
return static_cast<U>(std::stoll(_value));
}
std::string asString() const requires std::integral<T>
{
return std::to_string(_value);
}
std::string asString() const requires std::same_as<T, std::string>
{
return _value;
}
};
Usage example:
auto test()
{
{
auto m = Measurement<long>{};
m.setIntValue<int>(24);
int a = m.asInt<int>();
std::string s = m.asString();
m.setStringValue("11");
a = m.asInt<int>();
s = m.asString();
}
{
auto m = Measurement<std::string>{};
m.setIntValue<int>(24);
int a = m.asInt<int>();
std::string s = m.asString();
m.setStringValue("11");
a = m.asInt<int>();
s = m.asString();
}
}
Template specialization
#include <string>
#include <type_traits>
template <class T, class Enable = void> class Measurement;
template <class T>
class Measurement<T, std::enable_if_t<std::is_integral_v<T>>>
{
private:
T _value{};
public:
Measurement() = default;
template <std::integral U>
void setIntValue(U value)
{
_value = static_cast<U>(value);
}
void setStringValue(const std::string& str)
{
_value = static_cast<T>(std::stoll(str));
}
template <std::integral U>
U asInt() const
{
return static_cast<U>(_value);
}
std::string asString() const requires std::integral<T>
{
return std::to_string(_value);
}
};
template <>
class Measurement<std::string>
{
private:
std::string _value{};
public:
Measurement() = default;
template <std::integral U>
void setIntValue(U value)
{
_value = std::to_string(value);
}
void setStringValue(std::string str)
{
_value = std::move(str);
}
template <std::integral U>
U asInt() const
{
return static_cast<U>(std::stoll(_value));
}
std::string asString() const
{
return _value;
}
};
Same usage as before.
I have to say that the class seems too bloated. I would instead have a class for storing the measurement and a separate utility to help convert between measurement and integers/string.

Covariant return type with primitive types

I have the following:
template<typename T>
class AbsContainer {
public:
virtual T operator[](ptrdiff_t) = 0;
};
template<typename T>
class SpecialContainer : public AbsContainer<T>, Box<pair<ptrdiff,T>> {
class Proxy;
public:
Proxy operator[](ptrdiff_t i) {
return Proxy(i, this);
};
};
template <typename T>
class SpecialContainer<T>::Proxy {
ptrdiff_t _i;
Box* _p;
public:
Proxy(ptrdiff_t i, Box* p) : _i(i), _p(p);
Proxy& operator=(const T& elm) {
_p->::insert(pair<ptrdiff,T>(_i,elm)); //defined in Box
}
};
main:
SpecialContainer<int> x;
x[2] = 3;
This doesn't compile, because in class SpecialContainer, the operator[] with the one in AbsContainer.
Ideally in concept, Proxy operator[] should be an override. So I tried resolving this by achieving covariant return type by making Proxy inherit from T. However it doesn't work since T can be a primitive type, and inheriting from a primitive type has no sense.
Error because of operator conflict :
error: conflicting return type specified for ‘specialContainer<T>::Proxy B<T>::operator[](std::ptrdiff_t) [with T = int; std::ptrdiff_t = long unsigned int]
Error because trying to inherit from parametric type T (int in this case):
error: base type ‘int’ fails to be a struct or class type
Is there any way in which can this be resolved ?
You can implement something very similar to covariant types even without any compiler support for actual covariant types. Here's how to do it.
#include <cstddef>
#include <map>
#include <string>
#include <iostream>
template<typename T>
class AbsContainer {
public:
T operator[](ptrdiff_t i) { return operator_Abs(i); }
virtual T operator_Abs(ptrdiff_t) = 0;
};
template<typename T>
class SpecialContainer : public AbsContainer<T>, std::map<ptrdiff_t, T> {
public:
class Proxy;
Proxy operator[](ptrdiff_t i) { return operator_Spec(i); }
T operator_Abs(ptrdiff_t i) override {
return operator_Spec(i).get();
}
virtual Proxy operator_Spec(ptrdiff_t i) {
return Proxy(i, this);
}
};
template <typename T>
class SpecialContainer<T>::Proxy {
ptrdiff_t _i;
std::map<ptrdiff_t, T>* _p;
public:
Proxy(ptrdiff_t i, std::map<ptrdiff_t, T>* p) : _i(i), _p(p) {};
Proxy& operator=(const T& elm) {
_p->insert(std::pair<ptrdiff_t,T>(_i,elm));
return *this;
}
T get() { return (*_p)[_i]; }
};
int main()
{
SpecialContainer<std::string> spec;
AbsContainer<std::string>& abs = spec;
auto k = spec[42];
k = "Hello World";
std::cout << abs[42] << "\n";
}

Deducing const from operator T &()

Issue is that different compiler's produces different output (clang/gcc) and so that makes me think that this usage is undefined behavour. However my goal is to deduce const when assigning reference.
Output with:
clang-3.6 -> not const
gcc-4.8.4 -> const
#include <iostream>
#include <type_traits>
struct AnyReference {
template <typename RT> AnyReference(RT &a_var) : _ptr(&a_var) {}
template <typename T> operator T &() const
{
if (std::is_const<T>::value) {
std::cout << "const\n";
}
else {
std::cout << "not const\n";
}
return *reinterpret_cast<T *>(_ptr);
}
void *_ptr;
};
int main()
{
int i(5);
AnyReference a(i);
const int &c = a;
}
One possibility based on idea of Ben Voight
struct AnyReference {
template <typename RT> AnyReference(RT &a_var) : _ptr(&a_var) {}
template <typename T> operator T &() const { return operatorTand<T>(); }
template <typename T> operator const T &() const
{
return operatorTand<const T>();
}
private:
template <typename T> T &operatorTand() const
{
if (std::is_const<T>::value) {
std::cout << "const\n";
}
else {
std::cout << "not const\n";
}
return *reinterpret_cast<T *>(_ptr);
}
void *_ptr;
};

Issue with temporaries and operator overloading

I am working on operator overloading to generate lazy object evaluation. For this reason class at operator+() doesn’t do more than storing reference of passed classes to evaluate later.
struct Base
{
virtual void expensive_func()
{
throw "cant use this";
};
Composer operator+(int i)
{
return Composer(*this, i);
}
}
struct Composer:public Base
{
Base& refBase;
int increment;
virtual void expensive_func()
{
heavy_work();
};
Composer(Base& a,int inc):
refBase(a),increment(inc)
{
}
}
struct D:public Base
{
...
}
And than problem arase
D a;
auto b = a + 2;
auto c = b + 3;
auto e = a + 3 + 4;
a.expensive_func(); //fine
b.expensive_func(); //fine
c.expensive_func(); //fine
e.expensive_func(); //segfault
On solution is to prevent such manoeuvres with
operator+(const Coposer&&,int) = delete;
But this just prevents doing something of what I would like to do
Full code: - I am building with gcc/g++ 4.8
#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
#include <memory>
namespace Intervals
{
template <typename T> struct ContainerMove; //forward declaration
template <typename T>
struct ContainerBase {
typedef T mT;
virtual T GetInterval (const T& val)
{
throw; //overload this
}
T Integrate(const T& start = T(0))
{
T r(0);
T next(start);
while(1)
{
T i = GetInterval(next);
if(i<0) //less that 0 is considered end
{
return r;
}
r+=i;
next=i;
}
}
ContainerMove<T> operator +(const T& left);
ContainerMove<T> operator -(const T& left);
virtual ~ContainerBase(){};
};
//lazy container of ContainerBase
template <typename T>
struct ContainerMove:public ContainerBase<T>
{
typedef ContainerBase<T> mConatinerBase;
const T mOffset;
mConatinerBase& mIntervalSet;
ContainerMove(mConatinerBase& intervalset, const T& offset)
:mOffset(offset),mIntervalSet(intervalset)
{}
virtual T GetInterval (const T& val)
{
auto i = mIntervalSet.GetInterval(val-mOffset);
if(i < 0)
{
return T(-1000);
}
return T(i+mOffset);
}
};
template <typename T>
ContainerMove<T> ContainerBase<T>::operator +(const ContainerBase<T>::mT& a)
{
return ContainerMove<T>(*this,a);
}
template <typename T>
ContainerMove<T> ContainerBase<T>::operator -(const ContainerBase<T>::mT& a)
{
return ContainerMove<T>(*this,-a);
}
/*
template <typename T>
ContainerMove<T> operator +(const ContainerMove<T>&& , const typename ContainerBase<T>::mT&) = delete;
template <typename T>
ContainerMove<T> operator -(const ContainerMove<T>&& , const typename ContainerBase<T>::mT&) = delete;
*/
template <class T>
struct Container:public ContainerBase<T>
{
typedef Container<T> mThisType;
typedef T mT;
typedef std::vector<T> SortedContainer_t;
SortedContainer_t mContainer;
template<class ForwardIter>
Container(ForwardIter begin,ForwardIter end)
:mContainer(begin,end)
{
}
T GetInterval (const T& val)
{
auto r = std::upper_bound(mContainer.begin(), mContainer.end(),val);
if (r == mContainer.end())
{
return T(-1000); //just as exeample <0 is ivalid value
}
return *r;
}
};
}
int main()
{
typedef float T;
typedef Intervals::Container<T> ContainerType;
std::vector<T> v,u;
const int N = 10;
for(int i=0;i<N;++i)
{
v.push_back(T(i));
}
auto c = ContainerType(v.begin(),v.end());
auto d=c+T(1);
auto e=c+T(2)+T(3); //this yelds segmentation after e.Integrate()
//std::cout << c.Integrate() << std::endl;
//std::cout << d.Integrate() << std::endl;
std::cout << e.Integrate() << std::endl; //crash here
return 0;
}