#define BOOST_YAP_CONVERSION_OPERATOR_TEMPLATE #include #include #include #include #include #include int allocations = 0; void * operator new (std::size_t size) { ++allocations; return malloc(size); } void operator delete (void * ptr) noexcept { free(ptr); } template struct lazy_vector_expr; struct take_nth { boost::yap::terminal operator() (boost::yap::terminal, lazy_vector_expr> const & expr); std::size_t n; }; template struct lazy_vector_expr { using this_type = lazy_vector_expr; static const boost::yap::expr_kind kind = Kind; Tuple elements; BOOST_YAP_USER_BINARY_OPERATOR_MEMBER(plus, this_type, ::lazy_vector_expr) BOOST_YAP_USER_BINARY_OPERATOR_MEMBER(minus, this_type, ::lazy_vector_expr) auto operator[] (std::size_t n) const { return boost::yap::evaluate(boost::yap::transform(*this, take_nth{n})); } }; boost::yap::terminal take_nth::operator() (boost::yap::terminal, lazy_vector_expr> const & expr) { double x = boost::yap::value(expr)[n]; return boost::yap::make_terminal(std::move(x)); } struct lazy_vector : lazy_vector_expr< boost::yap::expr_kind::terminal, boost::hana::tuple> > { template lazy_vector & operator+= (lazy_vector_expr const & rhs) { std::vector & this_vec = boost::yap::value(*this); for (int i = 0, size = (int)this_vec.size(); i < size; ++i) { this_vec[i] += rhs[i]; } return *this; } }; TEST(allocations, lazy_vector_alloc_text) { // GTest apparently allocates a ton of strings. We need to hit "reset" // here to measure the allocations in the part of the code we really care // about. allocations = 0; lazy_vector v1{{std::vector(4, 1.0)}}; lazy_vector v2{{std::vector(4, 2.0)}}; lazy_vector v3{{std::vector(4, 3.0)}}; double d1 = (v2 + v3)[2]; std::cout << d1 << "\n"; v1 += v2 - v3; std::cout << '{' << v1[0] << ',' << v1[1] << ',' << v1[2] << ',' << v1[3] << '}' << "\n"; EXPECT_EQ(allocations, 3); }