Last active
August 29, 2015 14:10
-
-
Save ahamez/7277f79dbaa7266c03ca to your computer and use it in GitHub Desktop.
expression templates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <functional> | |
#include <iterator> | |
// #include <iostream> | |
#include <initializer_list> | |
#include <vector> | |
/* ---------------------------------------------------------------------------------------------- */ | |
template <typename T, typename Exp> | |
struct vector_exp | |
{ | |
using container_type = std::vector<T>; | |
using value_type = typename container_type::value_type; | |
std::size_t size() const noexcept{return get().size();} | |
Exp& get() noexcept {return static_cast< Exp&>(*this);} | |
const Exp& get() const noexcept {return static_cast<const Exp&>(*this);} | |
}; | |
/* ---------------------------------------------------------------------------------------------- */ | |
template <typename T> | |
struct vector | |
: public vector_exp<T, vector<T>> | |
{ | |
using container_type = typename vector_exp<T, vector<T>>::container_type; | |
using value_type = typename container_type::value_type; | |
container_type impl; | |
vector(std::initializer_list<int> il) | |
noexcept | |
: impl{begin(il), end(il)} | |
{} | |
vector() = delete; | |
vector(const vector&) = default; | |
vector(vector&&) = default; | |
vector& | |
operator=(vector other) | |
{ | |
swap(*this, other); | |
return *this; | |
} | |
value_type operator[](std::size_t i) const noexcept {return impl[i];} | |
value_type& operator[](std::size_t i) noexcept {return impl[i];} | |
std::size_t | |
size() | |
const noexcept | |
{ | |
return impl.size(); | |
} | |
template <typename E> | |
vector(const vector_exp<T, E>& vector_exp) | |
{ | |
impl.resize(vector_exp.get().size()); | |
for (auto i = 0ul; i < vector_exp.get().size(); ++i) | |
{ | |
impl[i] = vector_exp.get()[i]; | |
} | |
} | |
// friend | |
// std::ostream& | |
// operator<<(std::ostream& os, const vector& a) | |
// { | |
// os << '{'; | |
// if (a.impl.size() > 0) | |
// { | |
// std::copy(begin(a.impl), std::prev(end(a.impl)), std::ostream_iterator<value_type>(os, ",")); | |
// os << *std::prev(end(a.impl)); | |
// } | |
// return os << '}'; | |
// } | |
friend | |
void | |
swap(vector& lhs, vector& rhs) | |
noexcept | |
{ | |
std::swap(lhs.impl, rhs.impl); | |
} | |
}; | |
/* ---------------------------------------------------------------------------------------------- */ | |
template <typename Op, typename T, typename E> | |
struct apply_value | |
: public vector_exp<T, apply_value<Op, T, E>> | |
{ | |
using value_type = typename vector_exp<T, apply_value<Op, T, E>>::value_type; | |
T value; | |
E sub; | |
apply_value() = delete; | |
apply_value(const apply_value&) = default; | |
apply_value(apply_value&&) = default; | |
apply_value& operator=(apply_value&&) = default; | |
apply_value(T v, vector_exp<T, E>&& e) | |
: value(v), sub(std::move(e.get())) | |
{} | |
apply_value(T v, const vector_exp<T, E>& e) | |
: value(v), sub(e.get()) | |
{} | |
std::size_t size() const noexcept {return sub.size();} | |
value_type operator[](std::size_t i) const noexcept {return Op{}(value, sub[i]);} | |
}; | |
#define CREATE_APPLY_OP(NAME,OP,FN) \ | |
\ | |
template <typename T, typename E> \ | |
using NAME = apply_value<FN, T, E>; \ | |
\ | |
template <typename T, typename E> \ | |
inline \ | |
NAME<T, E> \ | |
operator OP (T value, vector_exp<T, E>&& e) \ | |
{ \ | |
return {value, std::move(e)}; \ | |
} \ | |
\ | |
template <typename T, typename E> \ | |
inline \ | |
NAME<T, E> \ | |
operator OP (vector_exp<T, E>&& e, T value) \ | |
{ \ | |
return {value, std::move(e)}; \ | |
} \ | |
\ | |
template <typename T, typename E> \ | |
inline \ | |
NAME<T, E> \ | |
operator OP (T value, const vector_exp<T, E>& e) \ | |
{ \ | |
return {value, e}; \ | |
} \ | |
\ | |
template <typename T, typename E> \ | |
inline \ | |
NAME<T, E> \ | |
operator OP (const vector_exp<T, E>& e, T value) \ | |
{ \ | |
return {value, e}; \ | |
} | |
CREATE_APPLY_OP( value_plus_exp , + , std::plus<> ); | |
CREATE_APPLY_OP( value_minus_exp , - , std::minus<> ); | |
CREATE_APPLY_OP( value_multiplies_exp , * , std::multiplies<> ); | |
CREATE_APPLY_OP( value_divide_exp , / , std::divides<> ); | |
/* ---------------------------------------------------------------------------------------------- */ | |
template <typename Op, typename T, typename E1, typename E2> | |
struct binary | |
: public vector_exp<T, binary<Op, T, E1, E2>> | |
{ | |
using value_type = typename vector_exp<T, binary<Op, T, E1, E2>>::value_type; | |
E1 lhs; | |
E2 rhs; | |
binary(vector_exp<T, E1>&& l, vector_exp<T, E2>&& r) | |
: lhs(std::move(l.get())), rhs(std::move(r.get())) | |
{} | |
binary(const vector_exp<T, E1>& l, vector_exp<T, E2>&& r) | |
: lhs(l.get()), rhs(std::move(r.get())) | |
{} | |
binary(vector_exp<T, E1>&& l, const vector_exp<T, E2>& r) | |
: lhs(std::move(l.get())), rhs(r.get()) | |
{} | |
binary(const vector_exp<T, E1>& l, const vector_exp<T, E2>& r) | |
: lhs(l.get()), rhs(r.get()) | |
{} | |
std::size_t | |
size() | |
const noexcept | |
{ | |
return lhs.size(); | |
} | |
value_type | |
operator[](std::size_t i) | |
const noexcept | |
{ | |
return Op{}(lhs[i], rhs[i]); | |
} | |
}; | |
#define CREATE_BINARY_OP(NAME,OP,FN) \ | |
\ | |
template <typename T, typename E1, typename E2> \ | |
using NAME = binary<FN, T, E1, E2>; \ | |
\ | |
template <typename T, typename E1, typename E2> \ | |
inline \ | |
NAME<T, E1, E2> \ | |
operator OP (vector_exp<T, E1>&& lhs, vector_exp<T, E2>&& rhs) \ | |
{ \ | |
return {std::move(lhs), std::move(rhs)}; \ | |
} \ | |
\ | |
template <typename T, typename E1, typename E2> \ | |
inline \ | |
NAME<T, E1, E2> \ | |
operator OP (const vector_exp<T, E1>& lhs, vector_exp<T, E2>&& rhs) \ | |
{ \ | |
return {lhs, std::move(rhs)}; \ | |
} \ | |
\ | |
template <typename T, typename E1, typename E2> \ | |
inline \ | |
NAME<T, E1, E2> \ | |
operator OP (vector_exp<T, E1>&& lhs, const vector_exp<T, E2>& rhs) \ | |
{ \ | |
return {std::move(lhs), rhs}; \ | |
} \ | |
\ | |
template <typename T, typename E1, typename E2> \ | |
inline \ | |
NAME<T, E1, E2> \ | |
operator OP (const vector_exp<T, E1>& lhs, const vector_exp<T, E2>& rhs) \ | |
{ \ | |
return {lhs, rhs}; \ | |
} | |
CREATE_BINARY_OP( plus_exp , + , std::plus<> ); | |
CREATE_BINARY_OP( minus_exp , - , std::minus<> ); | |
CREATE_BINARY_OP( multiplies_exp , * , std::multiplies<> ); | |
CREATE_BINARY_OP( divides_exp , / , std::divides<> ); | |
/* ---------------------------------------------------------------------------------------------- */ | |
int main() | |
{ | |
using vec = vector<int>; | |
auto dummy = 0ul; | |
for (auto i = 0ul; i < 9999999; ++i) | |
{ | |
const auto v = (vec{0,1,2,3,4,5,6,7,8,9} * 3) | |
+ (vec{1,1,2,3,1,5,1,7,1,9} + vec{1,1,2,3,1,5,1,7,1,9}) | |
- (vec{9,9,9,9,9,9,9,9,9,9} / vec{2,2,2,2,2,2,2,2,2,2}) | |
; | |
dummy += v[0]; | |
} | |
return dummy; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <functional> | |
#include <iterator> | |
//#include <iostream> | |
#include <initializer_list> | |
#include <vector> | |
#include <boost/range/algorithm/for_each.hpp> | |
/* ---------------------------------------------------------------------------------------------- */ | |
using std::vector; | |
using boost::for_each; | |
template <typename T> | |
inline | |
vector<T> | |
operator*(int factor, vector<T>&& e) | |
{ | |
for_each(e, [&](auto& x){x *= factor;}); | |
return e; | |
} | |
template <typename T> | |
inline | |
vector<T> | |
operator*(vector<T>&& e, int factor) | |
{ | |
for_each(e, [&](auto& x){x *= factor;}); | |
return e; | |
} | |
template <typename T, typename E> | |
inline | |
vector<T> | |
operator*(int factor, const vector<T>& e) | |
{ | |
auto copy = e; | |
for_each(copy, [&](auto& x){x *= factor;}); | |
return copy; | |
} | |
template <typename T, typename E> | |
inline | |
vector<T> | |
operator*(const vector<T>& e, int factor) | |
{ | |
auto copy = e; | |
for_each(copy, [&](auto& x){x *= factor;}); | |
return copy; | |
} | |
/* ---------------------------------------------------------------------------------------------- */ | |
#define BINARY_OP(OP,STD) \ | |
\ | |
\ | |
template <typename T> \ | |
inline \ | |
vector<T> \ | |
operator OP (vector<T>&& lhs, vector<T>&& rhs) \ | |
{ \ | |
for ( auto lhs_cit = begin(lhs), rhs_cit = begin(rhs) \ | |
; lhs_cit != end(lhs); ++lhs_cit, ++rhs_cit) \ | |
{ \ | |
*lhs_cit = std::STD<void>{}(*lhs_cit, *rhs_cit); \ | |
} \ | |
return lhs; \ | |
} \ | |
\ | |
template <typename T> \ | |
inline \ | |
vector<T> \ | |
operator OP (const vector<T>& lhs, vector<T>&& rhs) \ | |
{ \ | |
for ( auto lhs_cit = begin(lhs), rhs_cit = begin(rhs) \ | |
; lhs_cit != end(lhs); ++lhs_cit, ++rhs_cit) \ | |
{ \ | |
*rhs_cit = std::STD<void>{}(*lhs_cit, *rhs_cit); \ | |
} \ | |
return rhs; \ | |
\ | |
} \ | |
\ | |
template <typename T> \ | |
inline \ | |
vector<T> \ | |
operator OP (vector<T>&& lhs, const vector<T>& rhs) \ | |
{ \ | |
for ( auto lhs_cit = begin(lhs), rhs_cit = begin(rhs) \ | |
; lhs_cit != end(lhs); ++lhs_cit, ++rhs_cit) \ | |
{ \ | |
*lhs_cit = std::STD<void>{}(*lhs_cit, *rhs_cit); \ | |
} \ | |
return lhs; \ | |
} \ | |
\ | |
template <typename T> \ | |
inline \ | |
vector<T> \ | |
operator OP (const vector<T>& lhs_, const vector<T>& rhs) \ | |
{ \ | |
auto lhs = lhs_; \ | |
for ( auto lhs_cit = begin(lhs), rhs_cit = begin(rhs) \ | |
; lhs_cit != end(lhs); ++lhs_cit, ++rhs_cit) \ | |
{ \ | |
*lhs_cit = std::STD<void>{}(*lhs_cit, *rhs_cit); \ | |
} \ | |
return lhs; \ | |
} \ | |
BINARY_OP( + , plus ); | |
BINARY_OP( * , multiplies ); | |
BINARY_OP( - , minus ); | |
BINARY_OP( / , divides ); | |
/* ---------------------------------------------------------------------------------------------- */ | |
int main() | |
{ | |
/*const auto display_vec = [&](auto& os, const auto& v) -> std::ostream& | |
{ | |
using value_type = typename std::decay_t<decltype(v)>::value_type; | |
os << '{'; | |
if (v.size() > 0) | |
{ | |
std::copy(begin(v), std::prev(end(v)), std::ostream_iterator<value_type>(os, ",")); | |
os << *std::prev(end(v)); | |
} | |
return os << '}'; | |
};*/ | |
using vec = vector<int>; | |
auto dummy = 0ul; | |
for (auto i = 0ul; i < 9999999; ++i) | |
{ | |
const auto v = (vec{0,1,2,3,4,5,6,7,8,9} * 3) | |
+ (vec{1,1,2,3,1,5,1,7,1,9} + vec{1,1,2,3,1,5,1,7,1,9}) | |
- (vec{9,9,9,9,9,9,9,9,9,9} / vec{2,2,2,2,2,2,2,2,2,2}) | |
; | |
dummy += v[0]; | |
} | |
return dummy; | |
} | |
// $ clang++ -O3 -std=c++1y -stdlib=libc++ no_expression_templates.cc -I /usr/local/boost-1.57/include | |
// $ time ./a.out | |
// real 0m12.170s | |
// user 0m12.165s | |
// sys 0m0.004s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment