Runtime polymorphism c++11 and operator overloading - c++11

Let's say I am trying to implement some math vector class.
As vector interface will be used in multiple places: array based vector, matrices return columns and rows as vector interface objects and etc.
I would like to overload +,- operators for my vectors. Each operator should return new constructed object of some vector implementation class.
But as you know operator overloading should return a value or a reference. I can not return a value, as I need runtime polymorphism, so I am left with references. But to have a reference that does not die after the function call object should be created in the heap.
So how should I manage the situation?
P.S. I could create a shared_ptr and return a reference to containing value, but it does not look like a good practice.
typedef unsigned int vector_idx_t;
template <class T, vector_idx_t size>
class vector {
public:
virtual ~vector();
virtual T& operator[](const vector_idx_t idx) = 0;
virtual vector<T, size>& operator+ (const T& a) const = 0;
virtual vector<T, size>& operator- (const T& a) const = 0;
virtual vector<T, size>& operator* (const T& a) const = 0;
virtual vector<T, size>& operator/ (const T& a) const = 0;
virtual vector<T, size>& operator+ (const vector<T, size>& vec2) const = 0;
virtual vector<T, size>& operator- (const vector<T, size>& vec2) const = 0;
};
template <class T, vector_idx_t size>
class array_vector: public vector<T, size> {
private:
std::array<T, size> m_elements;
public:
array_vector();
array_vector(std::array<T, size> elements);
array_vector(const vector<T, size>& vec2);
array_vector(std::initializer_list<T> elems);
virtual ~array_vector();
virtual T& operator[](const vector_idx_t idx) {
return m_elements[idx];
}
virtual vector<T, size>& operator+ (const T& a) const {
std::array<T, size> e;
for (vector_idx_t i = 0; i < size; ++i) {
e[i] = m_elements[i] + a;
}
auto v = std::make_shared<array_vector<T, size>>(elems);
return *v;
}
};

I suggest a slight modification to your design for accommodating the polymorphic nature of the implementation.
Don't make vector polymorphic.
Use a Data class to contain the implementation specific details of vector.
Make Data polymorphic.
That will allow you to return vectors by value or by reference, as appropriate to an interface.

Polymorphism by subtype is not the answer to all problems. I understand what are you trying to do but I don't exactly understand why a polymorphic by template solution is not enough and you need to have virtual operators (which don't mix well at all with polymorphism by subtype).
You want to be able to define operations on mixed types of vectors so that you can compute results between real containers and proxy to containers.
This first of all should require that you have a basic final type that you need, a proxy to a matrix column is not a real container but rather a view of a container, so adding two of them should return a real container (eg. a container backed by an actual std::array?).
A similar design could be managed by something like
template<typename ContainerType, typename ElementType>
class vector_of : public ContainerType
{
public:
vector_of(const ContainerType& container) : ContainerType(container) { }
vector_of<ContainerType, ElementType> operator+(const ElementType& a) const
{
vector_of<ContainerType, ElementType> copy = vector_of<ContainerType,ElementType>(*this);
std::for_each(copy.begin(), copy.end(), [&a](ElementType& element) { element += a; });
}
template<typename T>
vector_of<ContainerType, ElementType> operator+(const vector_of<T, ElementType>& a) const
{
vector_of<ContainerType, ElementType> copy(*this);
auto it = copy.begin();
auto it2 = a.begin();
while (it != copy.end() && it2 != a.end())
{
*it += *it2;
++it;
++it2;
}
return copy;
}
};
The trick here is that operator+ is a template method which accepts a generic container of ElementType elements. The code assumes that these kind of containers provide a begin and end methods which return an iterator (which is a smart choice in any case because it works well with STL).
With you can do things like:
class MatrixRowProxy
{
private:
int* data;
size_t length;
public:
MatrixRowProxy(int* data, size_t length) : data(data), length(length) { }
int* begin() const { return data; }
int* end() const { return data + length; }
};
vector_of<std::array<int, 5>, int> base = vector_of<std::array<int, 5>, int>({ 1, 2, 3, 4, 5 });
vector_of<std::vector<int>, int> element = vector_of<std::vector<int>, int>({ 2, 3, 4, 5, 6 });
int* data = new int[5] { 10, 20, 30, 40, 50};
vector_of<MatrixRowProxy, int> proxy = vector_of<MatrixRowProxy, int>(MatrixRowProxy(data, 5));
auto result = base + element + proxy;
for (const auto& t : result)
std::cout << t << std::endl;
So you can add heterogeneous kinds of vectors without the need of any virtual method.
Of course these methods require to create a new resulting object in the methods. This is done by copying this into a new vector_of<ContainerType, ElementType>. Nothing prevents you from adding a third template argument like VectorFactory which takes care of this so that you could use vectors which are only wrappers also on LHS of such operators.

Related

C++ How to add two vectors asynchronously

Good morning everyone.
I am new to C++ 11 multithreading theme and trying to write down a code that add two vectors of equal sizes in asynchronous way.
This means, if I have two vectors:
vector<int> fisrt = {1, 2, 3};
vector<int> second = {3, 2, 1};
first += second; // first = {4, 4, 4}
I wrote Paginator class which splits vector on "pages" with appropriate page_size.
My idea of vectors addition in asynchronous way is next: split vectors on pages with choosen page_size, and add pages of first and second vectors in asynchronous way.
Paginator class implementation
template<class Iter>
class IterRange {
public:
explicit IterRange(Iter first, Iter last) : first_(first), last_(last) {}
Iter begin() { return first_; }
const Iter begin() const { return first_; }
Iter end() { return last_; }
const Iter end() const { return last_; }
private:
Iter first_;
Iter last_;
};
template<class Iter>
IterRange<Iter> MakeIterRange(Iter first, Iter last) {
return IterRange<Iter> {first, last };
}
template<class Iter>
class Paginator {
public:
Paginator(Iter first, Iter last, size_t page_size) : page_size_(page_size) {
size_t pages_count = static_cast<size_t> (floor((double)distance(first, last) / page_size_));
pages_.reserve(pages_count);
size_t page_id = 0u;
Iter begin_page = first;
for (page_id, begin_page; page_id < pages_count; ++page_id, begin_page += page_size_) {
pages_.push_back(MakeIterRange( begin_page, begin_page + page_size ));
}
// If some elements less than page_size_ is left
if (begin_page != last) {
pages_.push_back(MakeIterRange(begin_page, begin_page + distance(begin_page, last)));
}
}
auto begin() { return pages_.begin(); }
auto begin() const { return pages_.begin(); }
auto end() { return pages_.end(); }
auto end() const { return pages_.end(); }
private:
size_t page_size_;
vector<IterRange<Iter>> pages_;
};
template<class Iter>
Paginator<Iter> MakePaginator(Iter first, Iter last, size_t page_size) {
return{ first, last, page_size };
}
template<class Container> // And the same for non constant Container
auto Paginate(const Container & c, size_t page_size) {
return MakePaginator(begin(c), end(c), page_size);
}
This Paginate procedure is used in operator+= in my Matrix class.
Matrix class fields are:
Matrix sizes along Ox and Oy direction respectively: size_t nx_, size_t ny_.
Vector of (nx_ * ny_) size, which stores all elements in matrix: vector body_.
Operator += for Matrix
template
inline Matrix & Matrix::operator+=(const Matrix & other) {
size_t threads_numb = thread::hardware_concurrency();
size_t page_size = static_cast<size_t> (ceil((double)body_.size() / threads_numb));
vector<future<void>> futures;
auto page_1 = page::Paginate(body_, page_size);
auto page_2 = page::Paginate(other.body_, page_size);
auto it_2 = page_2.begin();
for (auto it = page_1.begin(); it != page_1.end(); ++it, ++it_2) {
futures.push_back(
async([it, it_2] { transform(it->begin(), it->end(), it_2->begin(), it->begin(), plus<T>()); })
);
}
return *this;
}
But as a result I get iterator out of range error! How could I fix this?
P.S. Sorry for bad representation of first string in operator +=. Could not fix this problem :(

C++ STL; Iterate over class which contains STL container?

I have classes for linear algebra, specifically vectors and matrices. These contain among others std::vectors (or std::maps) as their 'data' fields.
Iterating over these in a range based for loop is easy. But I'd like to make these fields private and make iterating over my custom classes more natural, such that I can do range based for loops over the class itself.
I tried looking at the function definition of std::vector<>.begin() and such. Then I tried to implement it in such a way that all the iterator objects are forwaded from the std::vector<> fields, but to no avail. When I try to iterate over a constant instance of my class, with the following example;
int main() {
AlgebraLib::Vector A(4, true);
A[0] = 4;
A[1] = 5;
A[2] = 6;
A[3] = 7;
for (auto &&item : A) {
std::cout << item << std::endl;
}
const AlgebraLib::Vector B = A;
for (auto &&item : B) {
std::cout << item << std::endl;
}
return EXIT_SUCCESS;
}
... I get the following compiler error;
error: passing ‘const AlgebraLib::Vector’ as ‘this’ argument discards qualifiers [-fpermissive]
for (auto &&item : B) {
Basically, all iterators are defined like this:
std::vector<double>::iterator Vector::begin() {
return _VectorContents.begin();
}
std::vector<double>::iterator Vector::end() {
return _VectorContents.end();
}
std::vector<double>::reverse_iterator Vector::rbegin() {
return _VectorContents.rbegin();
}
std::vector<double>::reverse_iterator Vector::rend() {
return _VectorContents.rend();
}
std::vector<double>::const_iterator Vector::cbegin() const noexcept{
return _VectorContents.cbegin();
}
std::vector<double>::const_iterator Vector::cend() const noexcept{
return _VectorContents.cend();
}
std::vector<double>::const_reverse_iterator Vector::crbegin() const noexcept{
return _VectorContents.crbegin();
}
std::vector<double>::const_reverse_iterator Vector::crend() const noexcept{
return _VectorContents.crend();
}
And in my header Vector.hpp:
// Iterators
std::vector<double>::iterator begin();
std::vector<double>::iterator end();
std::vector<double>::reverse_iterator rbegin();
std::vector<double>::reverse_iterator rend();
std::vector<double>::const_iterator cbegin() const noexcept;
std::vector<double>::const_iterator cend() const noexcept;
std::vector<double>::const_reverse_iterator crbegin() const noexcept;
std::vector<double>::const_reverse_iterator crend() const noexcept;
I feel like I'm missing something here? I'm relatively new to C++, and I work at the moment with C++11.
The problem is that B is const, but the range-for loop only uses begin and end (not cbegin and cend) and your begin and end functions have no overloads that are const. This is a problem because only functions that are marked as const can be called on const objects.
The solution is simple: Just add such overloads
std::vector<double>::const_iterator begin() const;
std::vector<double>::const_iterator end() const;
The implementation of these overloads will be just the same as for the non-const functions.

Custom thread local STL allocator with Boost pool allocator

We have a huge legacy code base which is multithreaded and uses vectors extensively. To cut down the time spent in dynamic memory allocation, we are moving to a pools. The plan is to use Boost small vector with a custom allocator. The custom allocator will create a thread local pool per each container type. I have implemented a custom allocator based on the above idea and tested it. For some reason, the code falls in an infinite pattern inside find_prev method in Boost simple segregated storage. There are lots of places where there is a nesting of containers, like vector>> etc. Is this the right way of defining allocator ??
template<typename T, typename allocatorType>
class customAllocator
{
public:
static thread_local allocatorType *_allocator;
typedef T value_type;
typedef allocatorType allocator_Type;
template <class X> struct rebind
{
typedef customAllocator<X, allocatorType> other;
};
customAllocator()
{
_allocator = new allocatorType;
assert(_allocator);
return;
}
~customAllocator()
{
delete _allocator;
_allocator = nullptr;
return;
}
template<class X, class Y> customAllocator(const customAllocator<X, Y>& other)
{
_allocator = other._allocator;
return;
}
template<class X, class Y> customAllocator(customAllocator<X, Y>&& other)
{
_allocator = other._allocator;
other._allocator = nullptr;
return;
}
template<class X, class Y> customAllocator& operator=(const customAllocator<X, Y>& other)
{
_allocator = other._allocator;
return *this;
}
template<class X, class Y> customAllocator& operator=(customAllocator<X, Y>&& other)
{
_allocator = other._allocator;
other._allocator = nullptr;
return *this;
}
T* allocate(size_t n)
{
return _allocator->allocate(n * sizeof(T));
}
void deallocate(T* ptr, size_t n)
{
_allocator->deallocate(ptr, n);
return;
}
template<class X, class Y> bool operator==(const customAllocator<X, Y>& other) const noexcept
{ return (*this._allocator == other.allocator); }
template<class X, class Y> bool operator!=(const customAllocator<X, Y>& other) const noexcept
{ return !(*this._allocator == other._allocator); }
};
template <typename T1, typename T2>
thread_local T2 *customAllocator<T1, T2>::_allocator = nullptr;
using smallVector = boost::container::small_vector<
T,
DEFAULT_SMALL_VECTOR_LENGTH,
customAllocator<T,
boost::pool_allocator<
T,
boost::default_user_allocator_new_delete,
boost::details::pool::null_mutex,
2,
4
>>>;

Custom allocator with hard limits

I want to replace some code that uses boost::interprocess shared memory. One advantage of shared memory is that you can impose limits on the maximum amount of memory it can use. I'm looking for a custom allocator, based off std::allocator that can do this.
Only particular classes in the program will use this allocator, everything else uses the defaulted std::allocator and are only limited by available RAM.
I'm trying to write one of my own but I'm running into issues, mainly with how to share state among the allocator copies that are created by STL containers. State includes the number of free bytes remaining and the maximum size the allocator can use. I thought I could get away with making them thread_local but then several different instances of the same class will all allocate and deallocate from the same limited heap, which is not what I want. I'm beginning to think it's not possible, hence this question here. Neither contiguous allocation nor performance are major requirements for now.
The hard limit on the memory size cannot be a template parameter either, it's read from a config file.
Edit: The issue with sharing state is that some containers call the default constructor of the allocator type. Obviously this constructor cannot easily know anything about the outside world even if shared_ptr is used it will be nullptr initialised. For example, look at the source code for std::string::clear
g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
After following the hints above I came up with this which seems to work ok for POD types, but things fall apart when I try to make a Vector or Map that uses String:
#include <string>
#include <vector>
#include <map>
#include <atomic>
#include <memory>
struct SharedState
{
SharedState()
: m_maxSize(0),
m_bytesRemaining(0)
{
}
SharedState(std::size_t maxSize)
: m_maxSize(maxSize),
m_bytesRemaining(maxSize)
{
}
void allocate(std::size_t bytes) const {
if (m_bytesRemaining < bytes) {
throw std::bad_alloc();
}
m_bytesRemaining -= bytes;
}
void deallocate(std::size_t bytes) const {
m_bytesRemaining += bytes;
}
std::size_t getBytesRemaining() const {
return m_bytesRemaining;
}
const std::size_t m_maxSize;
mutable std::atomic<std::size_t> m_bytesRemaining;
};
// --------------------------------------
template <typename T>
class BaseLimitedAllocator : public std::allocator<T>
{
public:
using size_type = std::size_t;
using pointer = T*;
using const_pointer = const T*;
using propagate_on_container_move_assignment = std::true_type;
template <typename U>
struct rebind
{
typedef BaseLimitedAllocator<U> other;
};
BaseLimitedAllocator() noexcept = default;
BaseLimitedAllocator(std::size_t maxSize) noexcept
: m_state(new SharedState(maxSize)) {
}
BaseLimitedAllocator(const BaseLimitedAllocator& other) noexcept {
m_state = other.m_state;
}
template <typename U>
BaseLimitedAllocator(const BaseLimitedAllocator<U>& other) noexcept {
m_state = other.m_state;
}
pointer allocate(size_type n, const void* hint = nullptr) {
m_state->allocate(n * sizeof(T));
return std::allocator<T>::allocate(n, hint);
}
void deallocate(pointer p, size_type n) {
std::allocator<T>::deallocate(p, n);
m_state->deallocate(n * sizeof(T));
}
public:
std::shared_ptr<SharedState> m_state; // This must be public for the rebind copy constructor.
};
template <typename T, typename U>
inline bool operator==(const BaseLimitedAllocator<T>&, const BaseLimitedAllocator<U>&) {
return true;
}
template <typename T, typename U>
inline bool operator!=(const BaseLimitedAllocator<T>&, const BaseLimitedAllocator<U>&) {
return false;
}
struct LimitedAllocator : public BaseLimitedAllocator<char>
{
LimitedAllocator(std::size_t maxSize)
: BaseLimitedAllocator<char>(maxSize) {
}
template <typename U>
using Other = typename BaseLimitedAllocator<char>::template rebind<U>::other;
};
// -----------------------------------------
// Example usage:
class SomeClass
{
public:
using String = std::basic_string<char, std::char_traits<char>, LimitedAllocator::Other<char>>;
template <typename T>
using Vector = std::vector<T, LimitedAllocator::Other<T>>;
template <typename K, typename V>
using Map = std::map<K, V, std::less<K>, LimitedAllocator::Other<std::pair<const K, V>>>;
Complex()
: allocator(256),
s(allocator),
v(allocator),
m(std::less<int>(), allocator) // Cannot only specify the allocator. Annoying.
{
}
const LimitedAllocator allocator;
String s;
Vector<int> v;
Map<int, String> m;
};

Persistent expression templates with unique_ptr and matrices

I want to use expression templates to create a tree of objects that persists across statement. Building the tree initially involves some computations with the Eigen linear algebra library. The persistent expression template will have additional methods to compute other quantities by traversing the tree in different ways (but I'm not there yet).
To avoid problems with temporaries going out of scope, subexpression objects are managed through std::unique_ptr. As the expression tree is built, the pointers should be propagated upwards so that holding the pointer for the root object ensures all objects are kept alive. The situation is complicated by the fact that Eigen creates expression templates holding references to temporaries that go out of scope at the end of the statement, so all Eigen expressions must be evaluated while the tree is being constructed.
Below is a scaled-down implementation that seems to work when the val type is an object holding an integer, but with the Matrix type it crashes while constructing the output_xpr object. The reason for the crash seems to be that Eigen's matrix product expression template (Eigen::GeneralProduct) gets corrupted before it is used. However, none of the destructors either of my own expression objects or of GeneralProduct seems to get called before the crash happens, and valgrind doesn't detect any invalid memory accesses.
Any help will be much appreciated! I'd also appreciate comments on my use of move constructors together with static inheritance, maybe the problem is there somewhere.
#include <iostream>
#include <memory>
#include <Eigen/Core>
typedef Eigen::MatrixXi val;
// expression_ptr and derived_ptr: contain unique pointers
// to the actual expression objects
template<class Derived>
struct expression_ptr {
Derived &&transfer_cast() && {
return std::move(static_cast<Derived &&>(*this));
}
};
template<class A>
struct derived_ptr : public expression_ptr<derived_ptr<A>> {
derived_ptr(std::unique_ptr<A> &&p) : ptr_(std::move(p)) {}
derived_ptr(derived_ptr<A> &&o) : ptr_(std::move(o.ptr_)) {}
auto operator()() const {
return (*ptr_)();
}
private:
std::unique_ptr<A> ptr_;
};
// value_xpr, product_xpr and output_xpr: expression templates
// doing the actual work
template<class A>
struct value_xpr {
value_xpr(const A &v) : value_(v) {}
const A &operator()() const {
return value_;
}
private:
const A &value_;
};
template<class A,class B>
struct product_xpr {
product_xpr(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) :
a_(std::move(a).transfer_cast()), b_(std::move(b).transfer_cast()) {
}
auto operator()() const {
return a_() * b_();
}
private:
derived_ptr<A> a_;
derived_ptr<B> b_;
};
// Top-level expression with a matrix to hold the completely
// evaluated output of the Eigen calculations
template<class A>
struct output_xpr {
output_xpr(expression_ptr<derived_ptr<A>> &&a) :
a_(std::move(a).transfer_cast()), result_(a_()) {}
const val &operator()() const {
return result_;
}
private:
derived_ptr<A> a_;
val result_;
};
// helper functions to create the expressions
template<class A>
derived_ptr<value_xpr<A>> input(const A &a) {
return derived_ptr<value_xpr<A>>(std::make_unique<value_xpr<A>>(a));
}
template<class A,class B>
derived_ptr<product_xpr<A,B>> operator*(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) {
return derived_ptr<product_xpr<A,B>>(std::make_unique<product_xpr<A,B>>(std::move(a).transfer_cast(), std::move(b).transfer_cast()));
}
template<class A>
derived_ptr<output_xpr<A>> eval(expression_ptr<derived_ptr<A>> &&a) {
return derived_ptr<output_xpr<A>>(std::make_unique<output_xpr<A>>(std::move(a).transfer_cast()));
}
int main() {
Eigen::MatrixXi mat(2, 2);
mat << 1, 1, 0, 1;
val one(mat), two(mat);
auto xpr = eval(input(one) * input(two));
std::cout << xpr() << std::endl;
return 0;
}
Your problem appears to be that you are using someone else's expression templates, and storing the result in an auto.
(This happens in product_xpr<A>::operator(), where you call *, which if I read it right, is an Eigen multiplication that uses expression templates).
Expression templates are often designed to presume the entire expression will occur on a single line, and it will end with a sink type (like a matrix) that causes the expression template to be evaluated.
In your case, you have a*b expression template, which is then used to construct an expression template return value, which you later evaluate. The lifetime of temporaries passed to * in a*b are going to be over by the time you reach the sink type (matrix), which violates what the expression templates expect.
I am struggling to come up with a solution to ensure that all temporary objects have their lifetime extended. One thought I had was some kind of continuation passing style, where instead of calling:
Matrix m = (a*b);
you do
auto x = { do (a*b) pass that to (cast to matrix) }
replace
auto operator()() const {
return a_() * b_();
}
with
template<class F>
auto operator()(F&& f) const {
return std::forward<F>(f)(a_() * b_());
}
where the "next step' is passed to each sub-expression. This gets trickier with binary expressions, in that you have to ensure that the evaluation of the first expression calls code that causes the second sub expression to be evaluated, and then the two expressions are combined, all in the same long recursive call stack.
I am not proficient enough in continuation passing style to untangle this knot completely, but it is somewhat popular in the functional programming world.
Another approach would be to flatten your tree into a tuple of optionals, then construct each optional in the tree using a fancy operator(), and manually hook up the arguments that way. Basically do manual memory management of the intermediate values. This will work if the Eigen expression templates are either move-aware or do not have any self-pointers, so that moving at the point of construction doesn't break things. Writing that would be challenging.
Continuation passing style, suggested by Yakk, solves the problem and isn't too insane (not more insane than template metaprogramming in general anyhow). The double lambda evaluation for the arguments of binary expressions can be tucked away in a helper function, see binary_cont in the code below. For reference, and since it's not entirely trivial, I'm posting the fixed code here.
If somebody understands why I had to put a const qualifier on the F type in binary_cont, please let me know.
#include <iostream>
#include <memory>
#include <Eigen/Core>
typedef Eigen::MatrixXi val;
// expression_ptr and derived_ptr: contain unique pointers
// to the actual expression objects
template<class Derived>
struct expression_ptr {
Derived &&transfer_cast() && {
return std::move(static_cast<Derived &&>(*this));
}
};
template<class A>
struct derived_ptr : public expression_ptr<derived_ptr<A>> {
derived_ptr(std::unique_ptr<A> &&p) : ptr_(std::move(p)) {}
derived_ptr(derived_ptr<A> &&o) = default;
auto operator()() const {
return (*ptr_)();
}
template<class F>
auto operator()(F &&f) const {
return (*ptr_)(std::forward<F>(f));
}
private:
std::unique_ptr<A> ptr_;
};
template<class A,class B,class F>
auto binary_cont(const derived_ptr<A> &a_, const derived_ptr<B> &b_, const F &&f) {
return a_([&b_, f = std::forward<const F>(f)] (auto &&a) {
return b_([a = std::forward<decltype(a)>(a), f = std::forward<const F>(f)] (auto &&b) {
return std::forward<const F>(f)(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
});
});
}
// value_xpr, product_xpr and output_xpr: expression templates
// doing the actual work
template<class A>
struct value_xpr {
value_xpr(const A &v) : value_(v) {}
template<class F>
auto operator()(F &&f) const {
return std::forward<F>(f)(value_);
}
private:
const A &value_;
};
template<class A,class B>
struct product_xpr {
product_xpr(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) :
a_(std::move(a).transfer_cast()), b_(std::move(b).transfer_cast()) {
}
template<class F>
auto operator()(F &&f) const {
return binary_cont(a_, b_,
[f = std::forward<F>(f)] (auto &&a, auto &&b) {
return f(std::forward<decltype(a)>(a) * std::forward<decltype(b)>(b));
});
}
private:
derived_ptr<A> a_;
derived_ptr<B> b_;
};
template<class A>
struct output_xpr {
output_xpr(expression_ptr<derived_ptr<A>> &&a) :
a_(std::move(a).transfer_cast()) {
a_([this] (auto &&x) { this->result_ = x; });
}
const val &operator()() const {
return result_;
}
private:
derived_ptr<A> a_;
val result_;
};
// helper functions to create the expressions
template<class A>
derived_ptr<value_xpr<A>> input(const A &a) {
return derived_ptr<value_xpr<A>>(std::make_unique<value_xpr<A>>(a));
}
template<class A,class B>
derived_ptr<product_xpr<A,B>> operator*(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) {
return derived_ptr<product_xpr<A,B>>(std::make_unique<product_xpr<A,B>>(std::move(a).transfer_cast(), std::move(b).transfer_cast()));
}
template<class A>
derived_ptr<output_xpr<A>> eval(expression_ptr<derived_ptr<A>> &&a) {
return derived_ptr<output_xpr<A>>(std::make_unique<output_xpr<A>>(std::move(a).transfer_cast()));
}
int main() {
Eigen::MatrixXi mat(2, 2);
mat << 1, 1, 0, 1;
val one(mat), two(mat), three(mat);
auto xpr = eval(input(one) * input(two) * input(one) * input(two));
std::cout << xpr() << std::endl;
return 0;
}

Resources