/******************************************************************************* * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * * Copyright (C) 2012 by O. Parcollet * * TRIQS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * TRIQS. If not, see . * ******************************************************************************/ #ifndef TRIQS_CLEF_CORE_H #define TRIQS_CLEF_CORE_H #include #include #include #include #include #include #include #include #include #include #include #define TRIQS_CLEF_MAXNARGS 8 namespace triqs { namespace clef { typedef unsigned long long ull_t; template struct remove_cv_ref : std::remove_cv< typename std::remove_reference::type> {}; namespace tags { struct function_class{}; struct function{}; struct subscript{}; struct terminal{}; struct if_else{}; struct unary_op{}; struct binary_op{}; } /* --------------------------------------------------------------------------------------------------- * Placeholder and corresponding traits * --------------------------------------------------------------------------------------------------- */ template struct pair { static constexpr int p = i; typedef typename remove_cv_ref ::type value_type; value_type const & r; value_type const & rhs() const { return r;} pair (T const & t) : r(t){} }; template struct placeholder { static_assert( (N>=0) && (N<64) , "Placeholder number limited to [0,63]"); static constexpr int index = N; template pair operator = (RHS const & rhs) { return pair(rhs);} }; template struct is_placeholder : std::false_type {}; template struct is_placeholder > : std::true_type {}; template struct ph_set; template struct ph_set{static constexpr ull_t value= ph_set::value| ph_set::value;}; template struct ph_set {static constexpr ull_t value= 0;}; template struct ph_set> {static constexpr ull_t value= 1ull< struct ph_set > : ph_set>{}; /* --------------------------------------------------------------------------------------------------- * Detect if an object is a lazy expression * --------------------------------------------------------------------------------------------------- */ template struct is_any_lazy : std::false_type {}; template struct is_any_lazy > : std::true_type {}; template struct is_any_lazy< T > : std::false_type {}; template struct is_any_lazy< T&& > : is_any_lazy {}; template struct is_any_lazy< T& > : is_any_lazy {}; template struct is_any_lazy< T const > : is_any_lazy {}; template struct is_any_lazy : std::integral_constant::value || is_any_lazy::value> {}; // a trait that checks the type are value i.e. no &, && template struct has_no_ref : std::true_type {}; template struct has_no_ref : std::integral_constant::value)&& has_no_ref::value> {}; /* --------------------------------------------------------------------------------------------------- * Node of the expression tree * --------------------------------------------------------------------------------------------------- */ template struct expr { static_assert( has_no_ref::value , "Internal error : expr childs can not be reference "); typedef std::tuple< T ...> childs_t; childs_t childs; expr(expr const & x) = default; expr(expr && x) : childs(std::move(x.childs)) {} template explicit expr(Tag, Args&&...args) : childs(std::forward(args)...) {} template< typename Args > expr::type > operator[](Args && args) const { return expr::type> (tags::subscript(), *this,std::forward(args));} template< typename... Args > expr::type...> operator()(Args && ... args) const { return expr::type...>(tags::function(), *this,std::forward(args)...);} // only f(i,j) = expr is allowed, in case where f is a clef::function // otherwise use the << operator template typename std::enable_if::type>::value>::type operator= (RHS const & rhs) { *this << rhs;} template typename std::enable_if::type>::value>::type operator= (RHS const & rhs) = delete; expr & operator= (expr const & ) = delete; // no ordinary copy }; template struct ph_set< expr > : ph_set {}; template struct is_any_lazy< expr >: std::true_type {}; /* --------------------------------------------------------------------------------------------------- * The basic operations put in a template.... * --------------------------------------------------------------------------------------------------- */ template struct operation; /// Terminal template<> struct operation { template L operator()(L&& l) const { return std::forward(l);} }; /// Function call template<> struct operation { template auto operator()(F const & f, Args const & ... args) const -> decltype(f(args...)) { return f(args...);} template auto operator()(std::reference_wrapper const &f, Args const & ... args) const -> decltype(f.get()(args...)) { return f.get()(args...);} }; /// [ ] Call template<> struct operation { template auto operator()(F const & f, Args const & args) const -> decltype(f[args]) { return f[args];} template auto operator()(std::reference_wrapper const &f, Args const & args) const -> decltype(f.get()[args]) { return f.get()[args];} }; // all binary operators.... #define TRIQS_CLEF_OPERATION(TAG,OP)\ namespace tags { struct TAG : binary_op { static const char * name() { return BOOST_PP_STRINGIZE(OP);} };}\ template<> struct operation {\ template auto operator()(L const & l, R const & r) const -> decltype(l OP r) { return l OP r;}\ };\ template\ typename std::enable_if::value, expr::type,typename remove_cv_ref::type> >::type \ operator OP (L && l, R && r) { return expr::type,typename remove_cv_ref::type> (tags::TAG(),std::forward(l),std::forward(r));}\ TRIQS_CLEF_OPERATION(plus, +); TRIQS_CLEF_OPERATION(minus, -); TRIQS_CLEF_OPERATION(multiplies, *); TRIQS_CLEF_OPERATION(divides, /); TRIQS_CLEF_OPERATION(greater, >); TRIQS_CLEF_OPERATION(less, <); TRIQS_CLEF_OPERATION(leq, <=); TRIQS_CLEF_OPERATION(geq, >=); TRIQS_CLEF_OPERATION(eq, ==); #undef TRIQS_CLEF_OPERATION // all unary operators.... #define TRIQS_CLEF_OPERATION(TAG,OP)\ namespace tags { struct TAG : unary_op { static const char * name() { return BOOST_PP_STRINGIZE(OP);} };}\ template<> struct operation {\ template auto operator()(L const & l) const -> decltype(OP l) { return OP l;}\ };\ template\ typename std::enable_if::value, expr::type> >::type \ operator OP (L && l) { return expr::type> (tags::TAG(),std::forward(l));}\ TRIQS_CLEF_OPERATION(negate, -); TRIQS_CLEF_OPERATION(loginot, !); #undef TRIQS_CLEF_OPERATION /// the only ternary node : expression if template<> struct operation { template auto operator()(C const & c, A const & a, B const & b) const -> decltype((c?a:b)) { //static_assert(std::is_same::type,typename remove_cv_ref::type>::value, "Expression if : evaluation type must be the same"); return (c ? a: b); } }; // operator is : if_else( Condition, A, B) template expr::type,typename remove_cv_ref::type,typename remove_cv_ref::type> if_else(C && c, A && a, B && b) { return expr::type,typename remove_cv_ref::type,typename remove_cv_ref::type> (tags::if_else(),std::forward(c),std::forward(a),std::forward(b));} /* --------------------------------------------------------------------------------------------------- * Evaluation of the expression tree. * --------------------------------------------------------------------------------------------------- */ template struct operation2; template struct operation2 { typedef expr::type ...> rtype; rtype operator()(Args && ... args) const {return rtype (Tag(), std::forward(args)...);} }; template struct operation2 { typedef typename std::remove_reference(Args...)>::type>::type rtype; // remove the reference because of ternary if_else in which decltype returns a ref... rtype operator()(Args const & ... args) const {return operation()(args...); } }; // Generic case : do nothing (for the leaf of the tree except placeholder) template struct evaluator{ typedef T rtype; rtype operator()( T const & k, Pairs const &... pairs) {return k;} }; // placeholder template struct evaluator< placeholder, pair, Pairs... > { typedef evaluator< placeholder, Pairs...> eval_t; typedef typename eval_t::rtype rtype; rtype operator()(placeholder, pair const &, Pairs const& ... pairs) { return eval_t()(placeholder(), pairs...);} }; template struct evaluator< placeholder, pair, Pairs... > { typedef typename pair::value_type const & rtype; rtype operator()(placeholder, pair const & p, Pairs const& ...) { return p.r;} }; // general expr node template struct evaluator, Pairs...> { typedef operation2::rtype... >::value, typename evaluator::rtype... > OPTYPE; typedef typename OPTYPE::rtype rtype; #define AUX(z,p,unused) eval(std::get

(ex.childs),pairs...) #define IMPL(z, NN, unused) \ template< int arity = sizeof...(Childs)>\ typename std::enable_if< arity==NN, rtype>::type\ operator()(expr const & ex, Pairs const & ... pairs) const\ { return OPTYPE()(BOOST_PP_ENUM(NN,AUX,nil));} BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_INC(TRIQS_CLEF_MAXNARGS), IMPL, nil); #undef AUX #undef IMPL }; // The general eval function for expressions template typename evaluator::rtype eval (T const & ex, Pairs const &... pairs) { return evaluator()(ex, pairs...); } #ifdef TRIQS_SHORT_CIRCUIT_NOT_IMPLEMENTED // A short circuit if intersection of ph and is 0, no need to evaluate the whole tree...... template template typename std::enable_if< (ph_set::value & ph_set::value) !=0, typename evaluator,Pairs...>::rtype > ::type eval (expr const & ex, Pairs const &... pairs) { return evaluator, Pairs...>()(ex, pairs...); } template typename std::enable_if< (ph_set::value & ph_set::value) ==0, expr > ::type eval (expr const & ex, Pairs const &... pairs) { return ex; } #endif /* --------------------------------------------------------------------------------------------------- * make_function : transform an expression to a function * --------------------------------------------------------------------------------------------------- */ template< typename Expr, int... Is> struct make_fun_impl { Expr ex; // keep a copy of the expression make_fun_impl(Expr const & ex_) : ex(ex_) {} // gcc 4.6 crashes (!!!) on the first variant #define TRIQS_WORKAROUND_GCC46BUG #ifndef TRIQS_WORKAROUND_GCC46BUG template typename evaluator...>::rtype operator()(Args const &... args) const { return evaluator...>() ( ex, pair(args)...); } #else template struct __eval { typedef evaluator...> eval_t; typedef typename eval_t::rtype rtype; rtype operator()(Expr const &ex , Args const &... args) const { return eval_t() ( ex, pair(args)...); } }; template typename __eval::rtype operator()(Args const &... args) const { return __eval() ( ex, args...); } #endif }; template struct ph_filter; template struct ph_filter { static constexpr ull_t value = ph_filter::value & (~ (1ull< struct ph_filter { static constexpr ull_t value = x; }; template< typename Expr, int... Is> struct ph_set > { static constexpr ull_t value = ph_filter ::value, Is...>::value;}; template< typename Expr, int... Is> struct is_any_lazy > : std::integral_constant>::value !=0>{}; template< typename Expr, int... Is,typename... Pairs> struct evaluator, Pairs...> { typedef evaluator e_t; typedef make_fun_impl rtype; rtype operator()(make_fun_impl const & f, Pairs const & ... pairs) const { return rtype( e_t()(f.ex, pairs...));} }; template< typename Expr, typename ... Phs> make_fun_impl::type>::type,Phs::index...> make_function(Expr && ex, Phs...) { return {ex}; } namespace result_of { template< typename Expr, typename ... Phs> struct make_function { typedef make_fun_impl::type>::type,Phs::index...> type; }; } /* -------------------------------------------------------------------------------------------------- * make_function * x_ >> expression is the same as make_function(expression,x) * --------------------------------------------------------------------------------------------------- */ template make_fun_impl operator >> ( placeholder p, Expr&& ex) { return {ex}; } /* --------------------------------------------------------------------------------------------------- * Auto assign for () * --------------------------------------------------------------------------------------------------- */ // by default it is deleted = not implemented : every class has to define it... template void triqs_clef_auto_assign (T,F) = delete; // remove the ref_wrapper, terminal ... template void triqs_clef_auto_assign (std::reference_wrapper R ,F && f) { triqs_clef_auto_assign(R.get(),std::forward(f));} template void triqs_clef_auto_assign (expr const & t,F && f) { triqs_clef_auto_assign(std::get<0>(t.childs),std::forward(f));} // auto assign of an expr ? (for chain calls) : just reuse the same operator template void triqs_clef_auto_assign (expr && ex, RHS const & rhs) { ex << rhs;} template void triqs_clef_auto_assign (expr const & ex, RHS const & rhs) { ex << rhs;} // The case A(x_,y_) = RHS : we form the function (make_function) and call auto_assign (by ADL) template void operator<<(expr...> && ex, RHS && rhs) { triqs_clef_auto_assign(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr...> const & ex, RHS && rhs) { triqs_clef_auto_assign(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } // any other case e.g. f(x_+y_) = RHS etc .... which makes no sense : compiler will stop template void operator<<(expr && ex, RHS && rhs) = delete; template void operator<<(expr const & ex, RHS && rhs) = delete; /* --------------------------------------------------------------------------------------------------- * Auto assign for [] * --------------------------------------------------------------------------------------------------- */ // by default it is deleted = not implemented : every class has to define it... template void triqs_clef_auto_assign_subscript (T,F) = delete; // remove the ref_wrapper, terminal ... template void triqs_clef_auto_assign_subscript (std::reference_wrapper R ,F && f) { triqs_clef_auto_assign_subscript(R.get(),std::forward(f));} template void triqs_clef_auto_assign_subscript (expr const & t,F && f) { triqs_clef_auto_assign_subscript(std::get<0>(t.childs),std::forward(f));} // auto assign of an expr ? (for chain calls) : just reuse the same operator template void triqs_clef_auto_assign_subscript (expr const & ex, RHS const & rhs) { ex << rhs;} // Same thing for the [ ] template void operator<<(expr...> const & ex, RHS && rhs) { triqs_clef_auto_assign_subscript(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr const & ex, RHS && rhs) = delete; /* -------------------------------------------------------------------------------------------------- * Create a terminal node of an object. The reference is wrapped in a reference_wrapper... * --------------------------------------------------------------------------------------------------- */ template expr::type > make_expr(T && x){ return expr::type >(tags::terminal(), std::forward(x));} template auto lazy (T && x) -> decltype (make_expr(std::ref(x))) { return make_expr(std::ref(x)); } /* -------------------------------------------------------------------------------------------------- * Create a call node of an object * The object can be kept as a : a ref, a copy, a view * --------------------------------------------------------------------------------------------------- */ namespace result_of { template< typename Obj, typename... Args > struct make_expr_call : std::enable_if< is_any_lazy::value, expr::type, typename remove_cv_ref::type ...> > {}; } template< typename Obj, typename... Args > typename result_of::make_expr_call::type make_expr_call(Obj&& obj, Args &&... args) { return typename result_of::make_expr_call::type (tags::function(),std::forward(obj), std::forward(args)...);} /* -------------------------------------------------------------------------------------------------- * function class : stores any expression polymorphically * f(x_,y_ ) = an expression associates this expression dynamically to f, which * can then be used as a std::function of the same signature... * --------------------------------------------------------------------------------------------------- */ template class function; template class function : tags::function_class { typedef std::function std_function_type; mutable std::shared_ptr _exp; // CLEAN THIS MUTABLE ? mutable std::shared_ptr < std_function_type > _fnt_ptr; public: function():_fnt_ptr(new std_function_type ()){} template explicit function(Expr const & _e, X... x) : _exp(new Expr(_e)),_fnt_ptr(new std_function_type(make_function(_e, x...))){} ReturnType operator()(T const &... t) const { return (*_fnt_ptr)(t...);} template< typename... Args> typename triqs::clef::result_of::make_expr_call::type operator()( Args&&... args ) const { return triqs::clef::make_expr_call (*this,args...);} template friend void triqs_clef_auto_assign (function const & x, RHS rhs) { * (x._fnt_ptr) = std_function_type (rhs); x._exp = std::shared_ptr (new typename std::remove_cv::type (rhs.ex)); } }; /* -------------------------------------------------------------------------------------------------- * The macro to make any function lazy * TRIQS_CLEF_MAKE_FNT_LAZY (Arity,FunctionName ) : creates a new function in the triqs::lazy namespace * taking expressions (at least one argument has to be an expression) * The lookup happens by ADL, so IT MUST BE USED IN THE triqs::lazy namespace * --------------------------------------------------------------------------------------------------- */ #define TRIQS_CLEF_MAKE_FNT_LAZY(name)\ struct name##_lazy_impl { \ template auto operator()(A&&... a) const -> decltype (name(std::forward(a)...)) { return name(std::forward(a)...); }\ };\ template< typename... A> \ typename triqs::clef::result_of::make_expr_call::type name( A&& ... a) { return make_expr_call(name##_lazy_impl(),a...);} }} // namespace triqs::clef #endif