/******************************************************************************* * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * * Copyright (C) 2012-2013 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 #include #define TRIQS_CLEF_MAXNARGS 8 namespace triqs { namespace clef { typedef unsigned long long ull_t; namespace tags { struct function_class{}; struct function{}; struct subscript{}; struct terminal{}; struct if_else{}; struct unary_op{}; struct binary_op{}; } // Compute the type to put in the expression tree. // If T is a lvalue reference, pack it into a reference_wrapper, unless force_copy_in_expr::value == true // If T is an rvalue reference, we store it as the type (using move semantics). template struct force_copy_in_expr : std::false_type{}; template struct force_copy_in_expr : force_copy_in_expr{}; template< class T > struct expr_storage_t {typedef T type;}; template< class T > struct expr_storage_t : std::conditional::value, typename std::remove_const::type ,std::reference_wrapper>{}; template< class T > struct expr_storage_t {typedef T type;}; template< class T > struct expr_storage_t {typedef T type;}; template< class T > struct expr_storage_t {typedef T type;}; /* --------------------------------------------------------------------------------------------------- * Placeholder and corresponding traits * --------------------------------------------------------------------------------------------------- */ template class pair; // forward // a placeholder is an empty struct, labelled by an int. template struct placeholder { static_assert( (N>=0) && (N<64) , "Placeholder number limited to [0,63]"); static constexpr int index = N; template pair operator = (RHS && rhs) { return {std::forward(rhs)};} }; // placeholder will always be copied (they are empty anyway). template< int N > struct force_copy_in_expr> : std::true_type{}; // represent a couple (placeholder, value). template struct pair { U rhs; static constexpr int p = N; typedef typename remove_cv_ref ::type value_type; }; // ph_set is a trait that given a pack of type, returns the set of placeholders they contain // it returns a int in binary coding : bit N in the int is 1 iif at least one T is lazy and contains placeholder 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>{}; // in_any_lazy : trait to detect if any of Args 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> {}; template constexpr bool ClefExpression() { return is_any_lazy::value;} template struct is_clef_expression : is_any_lazy{}; /* --------------------------------------------------------------------------------------------------- * Node of the expression tree * --------------------------------------------------------------------------------------------------- */ template struct expr { // T can be U, U & (a reference or a value). typedef std::tuple childs_t; childs_t childs; expr(expr const & x) = default; expr(expr && x) noexcept : childs(std::move(x.childs)) {} // a constructor with the Tag make it unambiguous with other constructors... template expr(Tag, Args&&...args) : childs(std::forward(args)...) {} // [] returns a new lazy expression, with one more layer template expr::type > operator[](Args && args) const { return {tags::subscript(), *this,std::forward(args)};} // () also ... template< typename... Args > expr::type...> operator()(Args && ... args) const { return {tags::function(), *this,std::forward(args)...};} // assignement is in general deleted expr & operator= (expr const &) = delete; // no ordinary assignment expr & operator= (expr &&) = default; // move assign ok // however, this is ok in the case f(i,j) = expr, where f is a clef::function template ENABLE_IF(std::is_base_of::type>) operator= (RHS const & rhs) { *this << rhs;} template DISABLE_IF(std::is_base_of::type>) operator= (RHS const & rhs) = delete; }; // set some traits template struct ph_set< expr > : ph_set {}; template struct is_any_lazy< expr >: std::true_type {}; // if we want that subexpression are copied ? template struct force_copy_in_expr< expr > : std::true_type{}; /* --------------------------------------------------------------------------------------------------- * The basic operations put in a template.... * --------------------------------------------------------------------------------------------------- */ template struct operation; // a little function to clean the reference_wrapper template U _cl(U && x) { return std::forward(x);} template auto _cl(std::reference_wrapper x) DECL_AND_RETURN(x.get()); /// Terminal template<> struct operation { template L operator()(L&& l) const { return std::forward(l);} }; /// Function call template<> struct operation { template auto operator()(F&& f, Args&&... args) const DECL_AND_RETURN(_cl(std::forward(f))(_cl(std::forward(args))...)); }; /// [ ] Call template<> struct operation { template auto operator()(F&& f, Args&& args) const DECL_AND_RETURN(_cl(std::forward(f))[_cl(std::forward(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 && l, R && r) const DECL_AND_RETURN ( _cl(std::forward(l)) OP _cl(std::forward(r)));\ };\ template\ typename std::enable_if::value, expr::type,typename expr_storage_t::type> >::type \ operator OP (L && l, R && r) { return {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 && l) const DECL_AND_RETURN (OP _cl(std::forward(l)));\ };\ template\ typename std::enable_if::value, expr::type> >::type \ operator OP (L && l) { return {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 { // A and B MUST be the same template A operator()(C const & c, A const & a, B const & b) const {return _cl(c) ? _cl(a): _cl(b);} //template auto operator()(C const & c, A const & a, B const & b) const DECL_AND_RETURN (_cl(c) ? _cl(a): _cl(b)); }; // operator is : if_else( Condition, A, B) template expr::type,typename expr_storage_t::type,typename expr_storage_t::type> if_else(C && c, A && a, B && b) { return {tags::if_else(),std::forward(c),std::forward(a),std::forward(b)};} /* --------------------------------------------------------------------------------------------------- * Evaluation of the expression tree. * --------------------------------------------------------------------------------------------------- */ template struct _or; template struct _or : std::integral_constant::value>{}; template<> struct _or<> : std::false_type{}; // Generic case : do nothing (for the leaf of the tree including placeholder) template struct evaluator{ typedef is_any_lazy is_lazy; T 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::is_lazy is_lazy; auto operator()(placeholder, pair const &, Pairs const& ... pairs) DECL_AND_RETURN( eval_t()(placeholder(), pairs...)); }; template struct evaluator< placeholder, pair, Pairs... > { typedef std::false_type is_lazy; T operator()(placeholder, pair const & p, Pairs const& ...) { return p.rhs;} }; template struct operation2; template struct operation2 { template expr::type ...> operator()(Args && ... args) const { return {Tag(), std::forward(args)...}; } }; template struct operation2 { template auto operator()(Args && ... args) const DECL_AND_RETURN( operation()(std::forward(args)...)); //void operator() (...) const {} }; // general expr node template struct evaluator_node_gal; template struct evaluator_node_gal<1, expr, Pairs...> { typedef _or::is_lazy... > is_lazy; typedef operation2 OPTYPE; auto operator()(expr const & ex, Pairs const & ... pairs) const DECL_AND_RETURN (OPTYPE()(eval(std::get<0>(ex.childs),pairs...))); }; template struct evaluator_node_gal<2, expr, Pairs...> { typedef _or::is_lazy... > is_lazy; typedef operation2 OPTYPE; auto operator()(expr const & ex, Pairs const & ... pairs) const DECL_AND_RETURN (OPTYPE()(eval(std::get<0>(ex.childs),pairs...),eval(std::get<1>(ex.childs),pairs...))); }; #define AUX(z,p,unused) eval(std::get

(ex.childs),pairs...) #define IMPL(z, NN, unused) \ template struct evaluator_node_gal, Pairs...> {\ typedef _or::is_lazy... > is_lazy;\ typedef operation2 OPTYPE;\ auto operator()(expr const & ex, Pairs const & ... pairs) const\ DECL_AND_RETURN ( OPTYPE()(BOOST_PP_ENUM(NN,AUX,nil)));\ }; BOOST_PP_REPEAT_FROM_TO(3,BOOST_PP_INC(TRIQS_CLEF_MAXNARGS), IMPL, nil); #undef AUX #undef IMPL template struct evaluator, Pairs...> : evaluator_node_gal, Pairs...>{}; // The general eval function for expressions template auto eval (T const & ex, Pairs const &... pairs) DECL_AND_RETURN( evaluator()(ex, pairs...)); /* --------------------------------------------------------------------------------------------------- * 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_) {} template auto operator()(Args &&... args) const DECL_AND_RETURN( evaluator...>() ( ex, pair{std::forward(args)}...)); }; // values of the ph, excluding the Is ... 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> struct force_copy_in_expr > : std::true_type{}; template< typename Expr, typename ... Phs> make_fun_impl::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,Phs::index...> type; }; } template< typename Expr, int... Is,typename... Pairs> struct evaluator, Pairs...> { typedef evaluator e_t; typedef std::integral_constant >::value != ph_set::value> is_lazy; auto operator()(make_fun_impl const & f, Pairs const & ... pairs) const DECL_AND_RETURN( make_function( e_t()(f.ex, pairs...),placeholder()...)); }; template struct ph_list {}; template ph_list var( placeholder ...) { return {};} template auto operator >> (ph_list, Expr const & ex) DECL_AND_RETURN( make_function(ex, placeholder()...)); /* -------------------------------------------------------------------------------------------------- * 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()...)); } template void operator<<(expr...> & 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 && ex, RHS const & rhs) { ex << rhs;} 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...> && ex, RHS && rhs) { triqs_clef_auto_assign_subscript(std::get<0>(ex.childs), make_function(std::forward(rhs), placeholder()...)); } template void operator<<(expr && ex, RHS && rhs) = delete; template void operator<<(expr const & ex, RHS && rhs) = delete; /* -------------------------------------------------------------------------------------------------- * Create a terminal node of an object. the from clone version force copying the object * --------------------------------------------------------------------------------------------------- */ // make a node with the ref, unless it is an rvalue (which is moved). template expr::type > make_expr(T && x){ return {tags::terminal(), std::forward(x)};} // make a node from a copy of the object template expr::type > make_expr_from_clone(T && x){ return {tags::terminal(), std::forward(x)};} /* -------------------------------------------------------------------------------------------------- * Create a call node of an object * The object can be kept as a : a ref, a copy, a view * --------------------------------------------------------------------------------------------------- */ template struct arity { static constexpr int value =-1;}; namespace _result_of { template< typename Obj, typename... Args > struct make_expr_call : std::enable_if< is_any_lazy::value, expr::type, typename expr_storage_t::type ...> > { static_assert (((arity::value==-1) || (arity::value == sizeof...(Args))), "Object called with a wrong number of arguments"); }; } template< typename Obj, typename... Args > typename _result_of::make_expr_call::type make_expr_call(Obj&& obj, Args &&... args) { return {tags::function(),std::forward(obj), std::forward(args)...};} /* -------------------------------------------------------------------------------------------------- * Create a [] call (subscript) node of an object * The object can be kept as a : a ref, a copy, a view * --------------------------------------------------------------------------------------------------- */ namespace _result_of { template< typename Obj, typename Arg> struct make_expr_subscript : std::enable_if< is_any_lazy::value, expr::type, typename expr_storage_t::type> > {}; } template< typename Obj, typename Arg> typename _result_of::make_expr_subscript::type make_expr_subscript(Obj&& obj, Arg && arg) { return {tags::subscript(),std::forward(obj), std::forward(arg)};} /* -------------------------------------------------------------------------------------------------- * 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{std::make_shared ()}{} 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> auto operator()( Args&&... args ) const DECL_AND_RETURN(make_expr_call (*this,std::forward(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)); } }; template struct force_copy_in_expr > : std::true_type{}; /* -------------------------------------------------------------------------------------------------- * 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 DECL_AND_RETURN (name(std::forward(a)...));\ };\ template< typename... A> \ auto name( A&& ... a) DECL_AND_RETURN(make_expr_call(name##_lazy_impl(),std::forward(a)...)); #define TRIQS_CLEF_IMPLEMENT_LAZY_METHOD(TY,name)\ struct __clef_lazy_method_impl_##name { \ TY * _x;\ template auto operator()(A&&... a) const DECL_AND_RETURN (_x->name(std::forward(a)...));\ friend std::ostream & operator<<(std::ostream & out, __clef_lazy_method_impl_##name const & x) { return out< \ auto name( A&& ... a) DECL_AND_RETURN (make_expr_call(__clef_lazy_method_impl_##name{this},std::forward(a)...)); #define TRIQS_CLEF_IMPLEMENT_LAZY_CALL(...)\ template< typename... Args>\ auto operator()(Args&&... args ) const & DECL_AND_RETURN(make_expr_call (*this,std::forward(args)...));\ \ template< typename... Args>\ auto operator()(Args&&... args ) & DECL_AND_RETURN(make_expr_call (*this,std::forward(args)...));\ \ template< typename... Args>\ auto operator()(Args&&... args ) && DECL_AND_RETURN(make_expr_call (std::move(*this),std::forward(args)...));\ }} // namespace triqs::clef #endif