1 /*! 2 * \file include/TFEL/Math/ExpressionTemplates/Expr.hxx 3 * \brief 4 * \author Thomas Helfer 5 * \date 04 févr. 2015 6 * \copyright Copyright (C) 2006-2018 CEA/DEN, EDF R&D. All rights 7 * reserved. 8 * This project is publicly released under either the GNU GPL Licence 9 * or the CECILL-A licence. A copy of thoses licences are delivered 10 * with the sources of TFEL. CEA or EDF may also distribute this 11 * project under specific licensing conditions. 12 */ 13 14 #ifndef LIB_TFEL_MATH_EXPRESSION_EXPR_HXX 15 #define LIB_TFEL_MATH_EXPRESSION_EXPR_HXX 16 17 #include<type_traits> 18 #include"TFEL/Metaprogramming/ResultOf.hxx" 19 #include"TFEL/Math/General/BasicOperations.hxx" 20 #include"TFEL/Math/General/ComputeObjectTag.hxx" 21 #include"TFEL/Math/General/ConceptRebind.hxx" 22 #include"TFEL/Math/General/ComputeUnaryResult.hxx" 23 #include"TFEL/Math/General/ComputeBinaryResult.hxx" 24 25 namespace tfel{ 26 27 namespace math{ 28 29 /*! 30 * an helper structure used for the partial specialisation of the 31 * Expr class. 32 * \tparam T : type of unary operation argument 33 * \tparam Op : the unary operation 34 */ 35 template<typename T1,typename Op> 36 struct UnaryOperation; 37 /*! 38 * an helper structure used for the partial specialisation of the 39 * Expr class. 40 * \tparam T1 : type of the left argument of the binary operation 41 * \tparam T2 : type of the right argument of the binary operation 42 * \tparam Op : the binary operation 43 */ 44 template<typename T1,typename T2,typename Op> 45 struct BinaryOperation; 46 /*! 47 * an helper structure used for the partial specialisation of the 48 * Expr class. 49 * \tparam T1 : type of the left argument of the binary operation 50 * \tparam T2 : type of the right argument of the binary operation 51 * \tparam Op : the binary operation 52 * \pre T1 must be a scalar 53 */ 54 template<typename T1,typename T2,typename Op> 55 struct ScalarObjectOperation; 56 /*! 57 * an helper structure used for the partial specialisation of the 58 * Expr class. 59 * \tparam T1 : type of the left argument of the binary operation 60 * \tparam T2 : type of the right argument of the binary operation 61 * \tparam Op : the binary operation 62 * \pre T1 must be a scalar 63 */ 64 template<typename T1,typename T2,typename Op> 65 struct ObjectScalarOperation; 66 /*! 67 * an helper structure used for the partial specialisation of the 68 * Expr class. 69 * \tparam T1 : type of the left argument of the diadic product 70 * \tparam T2 : type of the right argument of the diadic product 71 */ 72 template<typename T1,typename T2> 73 struct DiadicProductOperation; 74 75 /*! 76 * an helper class 77 */ 78 struct ExprBase 79 { 80 protected: 81 /*! 82 * a simple metafunction defining how an argument of an 83 * operation is stored in the Expr class. 84 */ 85 template<typename T> 86 using ArgumentStorage = typename std::conditional<std::is_rvalue_reference<T>::value, 87 const typename std::decay<T>::type, 88 const typename std::decay<T>::type&>::type; 89 }; // end of strut ExprBase 90 91 /*! 92 * \brief An helper class which defines array-like access operator 93 * by relying on the operator() of the derived class. 94 * \tparam Child : child class 95 */ 96 template<typename Child> 97 struct ExprWithArrayAccessOperator 98 { 99 // /*! 100 // * \brief array like access operator 101 // * \param[in] i : index 102 // */ 103 // auto operator[](const typename Child::size_type i) const 104 // -> decltype(std::declval<const Child>()(i)) 105 // { 106 // return static_cast<const Child&>(this).operator()(i); 107 // } 108 // /*! 109 // * \brief array like access operator 110 // * \param[in] i : index 111 // */ 112 // auto operator[](const size_type i) 113 // -> decltype(std::declval<Child>()(i)) 114 // { 115 // return static_cast<const Child&>(this).operator()(i); 116 // } 117 }; // end of struct ExprWithArrayAccessOperator 118 119 /*! 120 * \brief an Expr object allows the lazy evaluation of a mathematical 121 * operation. 122 * \tparam ResultType : the type of the result of the operation 123 * \tparam Operation : Operation to be performed 124 * 125 * Partial specialisation are provided for: 126 * - unary operations (negation, function call) 127 * - binary operations 128 */ 129 template<typename ResultType,typename Operation> 130 struct Expr 131 : public ConceptRebind<typename ComputeObjectTag<ResultType>::type, 132 Expr<ResultType,Operation>>::type, 133 public Operation 134 { 135 //! a simple alias 136 typedef typename Operation::value_type value_type; 137 //! a simple alias 138 typedef typename Operation::size_type size_type; 139 /*! 140 * default constructor 141 * \param[in] args : arguments of Operation constructor 142 */ 143 template<typename... Args> Exprtfel::math::Expr144 explicit TFEL_MATH_INLINE Expr(Args&&... args) 145 : Operation(std::forward<Args>(args)...) 146 {} 147 /*! 148 * \brief array like access operator 149 * \param[in] i : index 150 * \note This operator is always defined, even tough it might not be 151 * valid 152 */ 153 value_type operator []tfel::math::Expr154 operator[](const size_type i) const 155 { 156 // may not be defined 157 static_assert(tfel::meta::IsConstCallable<Expr,size_type>::cond, 158 "Expr is not callable"); 159 return Operation::operator()(i); 160 } 161 /*! 162 * \brief array like access operator 163 * \param[in] i : index 164 * \note This operator is always defined, even tough it might not be 165 * valid 166 */ 167 value_type& operator []tfel::math::Expr168 operator[](const size_type i) 169 { 170 // may not be defined 171 static_assert(std::is_same<decltype(Operation::operator()(i)), 172 value_type&>::value, 173 "unexpected return value for Operation::operator(size_type)"); 174 return Operation::operator()(i); 175 } 176 177 using Operation::operator(); 178 using Operation::operator=; 179 }; 180 181 /*! 182 * an Expr object allows the lazy evaluation of an operation. 183 * \tparam ResultType : 184 * \tparam T1 : type of the left argument of the binary operation 185 * \tparam T2 : type of the right argument of the binary operation 186 * \tparam Op : the binary operation 187 */ 188 template<typename ResultType,typename T1,typename Op> 189 struct Expr<ResultType,UnaryOperation<T1,Op> > 190 : public ExprBase, 191 public ConceptRebind<typename ComputeObjectTag<ResultType>::type, 192 Expr<ResultType,UnaryOperation<T1,Op>>>::type 193 { 194 Expr() = delete; 195 Expr(const Expr&) = default; 196 Expr(Expr&&) = default; 197 Expr& operator=(const Expr&) = delete; 198 Expr& operator=(Expr&&) = delete; 199 //! type return by the access operator(s) 200 using value_type = typename ResultType::value_type; 201 //! type used by access operator(s) 202 using size_type = typename ResultType::size_type; 203 //! result type 204 using result_type = ResultType; 205 //! argument type 206 using argument_type = T1; 207 //! storage type of the argument 208 using argument_storage_type = ArgumentStorage<T1>; 209 /*! 210 * \param l : left argument of the binary operation 211 * \param r : right argument of the binary operation 212 */ Exprtfel::math::Expr213 TFEL_MATH_INLINE Expr(T1 l) 214 : a(l) 215 {} // end of Expr 216 /*! 217 * multidimensional access operator 218 */ 219 template<typename... Indexes> 220 TFEL_MATH_INLINE auto operator ()tfel::math::Expr221 operator()(const Indexes... i) const 222 -> UnaryOperationHandler<typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type>::value, 223 typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type, 224 typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type&&>::type,Op> 225 { 226 static_assert(isUnaryOperationResultTypeValid<typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type,Op>::value, 227 "invalid call to unary operation operator()"); 228 return Op::apply(this->a(i...)); 229 } // end of operator() 230 protected: 231 argument_storage_type a; //<! argument of the operation 232 }; // end of struct Expr<ResultType,UnaryOperation<T1,Op> > 233 234 /*! 235 * an Expr object allows the lazy evaluation of a binary 236 * operation. 237 * \tparam ResultType : 238 * \tparam T1 : type of the left argument of the binary operation 239 * \tparam T2 : type of the right argument of the binary operation 240 * \tparam Op : the binary operation 241 */ 242 template<typename ResultType,typename T1,typename T2,typename Op> 243 struct Expr<ResultType,BinaryOperation<T1,T2,Op> > 244 : public ExprBase, 245 public ConceptRebind<typename ComputeObjectTag<ResultType>::type, 246 Expr<ResultType,BinaryOperation<T1,T2,Op> > >::type 247 { 248 Expr() = delete; 249 Expr(const Expr&) = default; 250 Expr(Expr&&) = default; 251 Expr& operator=(const Expr&) = delete; 252 Expr& operator=(Expr&&) = delete; 253 //! type return by the access operator(s) 254 using value_type = typename ResultType::value_type; 255 //! type used by access operator(s) 256 using size_type = typename ResultType::size_type; 257 //! result type 258 using result_type = ResultType; 259 //! type of left hand side argument 260 using lhs_type = T1; 261 //! type of right hand side argument 262 using rhs_type = T2; 263 //! storage type of left hand side argument 264 using lhs_storage_type = ArgumentStorage<T1>; 265 //! storage type of right hand side argument 266 using rhs_storage_type = ArgumentStorage<T2>; 267 /*! 268 * \param l : left argument of the binary operation 269 * \param r : right argument of the binary operation 270 */ Exprtfel::math::Expr271 TFEL_MATH_INLINE Expr(T1 l,T2 r) 272 : a(l),b(r) 273 {} // end of Expr 274 /*! 275 * array-like access operator 276 */ 277 TFEL_MATH_INLINE value_type operator []tfel::math::Expr278 operator[](const size_type i) const 279 { 280 static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,size_type>::type, 281 typename tfel::meta::ResultOf<rhs_storage_type,size_type>::type,Op>::value, 282 "invalid call to unary operation operator[]"); 283 return Op::apply(this->a(i),this->b(i)); 284 } 285 /*! 286 * multi-dimensional operator acces 287 */ 288 template<typename... Indexes> 289 TFEL_MATH_INLINE 290 BinaryOperationHandler<typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type>::value, 291 typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type, 292 typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type&&>::type, 293 typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type>::value, 294 typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type, 295 typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type&&>::type,Op> operator ()tfel::math::Expr296 operator()(const Indexes... i) const 297 { 298 static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type, 299 typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type,Op>::value, 300 "invalid call to binary operation operator()"); 301 return Op::apply(this->a(i...),this->b(i...)); 302 } // end of operator() 303 protected: 304 lhs_storage_type a; //<! left hand side argument of the operation 305 rhs_storage_type b; //<! right hand side argument of the operation 306 }; // end of struct Expr<ResultType,BinaryOperation<T1,T2,Op> > 307 308 /*! 309 * an Expr object allows the lazy evaluation of a binary 310 * operation between a scalar and a mathematical object. 311 * \tparam ResultType : 312 * \tparam T1 : type of the left argument of the binary operation 313 * \tparam T2 : type of the right argument of the binary operation 314 * \tparam Op : the binary operation 315 */ 316 template<typename ResultType,typename T1, 317 typename T2,typename Op> 318 struct Expr<ResultType,ScalarObjectOperation<T1,T2,Op>> 319 : public ExprBase, 320 public ConceptRebind<typename ComputeObjectTag<ResultType>::type, 321 Expr<ResultType,ScalarObjectOperation<T1,T2,Op> > >::type 322 { 323 Expr() = delete; 324 Expr(const Expr&) = default; 325 Expr(Expr&&) = default; 326 Expr& operator=(const Expr&) = delete; 327 Expr& operator=(Expr&&) = delete; 328 //! type return by the access operator(s) 329 using value_type = typename ResultType::value_type; 330 //! type used by access operator(s) 331 using size_type = typename ResultType::size_type; 332 //! result type 333 using result_type = ResultType; 334 //! type of left hand side argument 335 using lhs_type = T1; 336 //! type of right hand side argument 337 using rhs_type = T2; 338 //! storage type of left hand side argument 339 using lhs_storage_type = ArgumentStorage<T1>; 340 //! storage type of right hand side argument 341 using rhs_storage_type = ArgumentStorage<T2>; 342 /*! 343 * \param l : left argument of the binary operation 344 * \param r : right argument of the binary operation 345 */ Exprtfel::math::Expr346 TFEL_MATH_INLINE Expr(T1 l,T2 r) 347 : a(l),b(r) 348 {} // end of Expr 349 /*! 350 * \brief array-like access operator 351 */ 352 TFEL_MATH_INLINE value_type operator []tfel::math::Expr353 operator[](const size_type i) const 354 { 355 static_assert(isBinaryOperationResultTypeValid<typename std::add_lvalue_reference<lhs_storage_type>::type, 356 typename tfel::meta::ResultOf<rhs_storage_type,size_type>::type,Op>::value, 357 "invalid call"); 358 return Op::apply(this->a,this->b(i)); 359 } 360 /*! 361 * \brief multi-dimensional operator acces 362 */ 363 template<typename... Indexes> 364 TFEL_MATH_INLINE 365 BinaryOperationHandler<typename std::add_lvalue_reference<lhs_storage_type>::type, 366 typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type>::value, 367 typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type, 368 typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type&&>::type,Op> operator ()tfel::math::Expr369 operator()(const Indexes... i) const 370 { 371 static_assert(isBinaryOperationResultTypeValid<typename std::add_lvalue_reference<lhs_storage_type>::type, 372 typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type,Op>::value, 373 "invalid call"); 374 return Op::apply(this->a,this->b(i...)); 375 } // end of operator() 376 protected: 377 const lhs_storage_type a; //<! left hand side argument of the operation 378 const rhs_storage_type b; //<! right hand side argument of the operation 379 }; // end of struct Expr<ResultType,ScalarObjectOperation<T1,T2,Op>> 380 381 /*! 382 * an Expr object allows the lazy evaluation of a binary 383 * operation between a mathematical object and scalar. 384 * \tparam ResultType : 385 * \tparam T1 : type of the left argument of the binary operation 386 * \tparam T2 : type of the right argument of the binary operation 387 * \tparam Op : the binary operation 388 */ 389 template<typename ResultType,typename T1, 390 typename T2,typename Op> 391 struct Expr<ResultType,ObjectScalarOperation<T1,T2,Op> > 392 : public ExprBase, 393 public ConceptRebind<typename ComputeObjectTag<ResultType>::type, 394 Expr<ResultType,ObjectScalarOperation<T1,T2,Op> > >::type 395 { 396 Expr() = delete; 397 Expr(const Expr&) = default; 398 Expr(Expr&&) = default; 399 Expr& operator=(const Expr&) = delete; 400 Expr& operator=(Expr&&) = delete; 401 //! type return by the access operator(s) 402 using value_type = typename ResultType::value_type; 403 //! type used by access operator(s) 404 using size_type = typename ResultType::size_type; 405 //! result type 406 using result_type = ResultType; 407 //! type of left hand side argument 408 using lhs_type = T1; 409 //! type of right hand side argument 410 using rhs_type = T2; 411 //! storage type of left hand side argument 412 using lhs_storage_type = ArgumentStorage<T1>; 413 //! storage type of right hand side argument 414 using rhs_storage_type = ArgumentStorage<T2>; 415 /*! 416 * \param l : left argument of the binary operation 417 * \param r : right argument of the binary operation 418 */ Exprtfel::math::Expr419 TFEL_MATH_INLINE Expr(T1 l,T2 r) 420 : a(l),b(r) 421 {} // end of Expr 422 /*! 423 * array-like access operator 424 */ 425 TFEL_MATH_INLINE value_type operator []tfel::math::Expr426 operator[](const size_type i) const 427 { 428 static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,size_type>::type, 429 typename std::add_lvalue_reference<rhs_storage_type>::type,Op>::value, 430 "invalid call"); 431 return Op::apply(this->a(i),this->b); 432 } 433 /*! 434 * multi-dimensional operator acces 435 */ 436 template<typename... Indexes> 437 TFEL_MATH_INLINE 438 BinaryOperationHandler<typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type>::value, 439 typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type, 440 typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type&&>::type, 441 typename std::add_lvalue_reference<rhs_storage_type>::type,Op> operator ()tfel::math::Expr442 operator()(const Indexes... i) const 443 { 444 static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type, 445 typename std::add_lvalue_reference<rhs_storage_type>::type,Op>::value, 446 "invalid call"); 447 return Op::apply(this->a(i...),this->b); 448 } // end of operator() 449 protected: 450 lhs_storage_type a; //<! left hand side argument of the operation 451 rhs_storage_type b; //<! right hand side argument of the operation 452 }; // end of struct Expr<ResultType,ObjectScalarOperation<T1,T2,Op>> 453 454 /*! 455 * an Expr object allows the lazy evaluation of a diadic product 456 * \tparam ResultType : 457 * \tparam T1 : type of the left argument of the diadic product 458 * \tparam T2 : type of the right argument of the diadic product 459 */ 460 template<typename ResultType,typename T1,typename T2> 461 struct Expr<ResultType,DiadicProductOperation<T1,T2>> 462 : public ExprBase, 463 public ConceptRebind<typename ComputeObjectTag<ResultType>::type, 464 Expr<ResultType,DiadicProductOperation<T1,T2>>>::type 465 { 466 Expr() = delete; 467 Expr(const Expr&) = default; 468 Expr(Expr&&) = default; 469 Expr& operator=(const Expr&) = delete; 470 Expr& operator=(Expr&&) = delete; 471 //! type return by the access operator(s) 472 using value_type = typename ResultType::value_type; 473 //! type used by access operator(s) 474 using size_type = typename ResultType::size_type; 475 //! result type 476 using result_type = ResultType; 477 //! type of left hand side argument 478 using lhs_type = T1; 479 //! type of right hand side argument 480 using rhs_type = T2; 481 //! storage type of left hand side argument 482 using lhs_storage_type = ArgumentStorage<T1>; 483 //! storage type of right hand side argument 484 using rhs_storage_type = ArgumentStorage<T2>; 485 /*! 486 * \param l : left argument of the binary operation 487 * \param r : right argument of the binary operation 488 */ Exprtfel::math::Expr489 TFEL_MATH_INLINE Expr(T1 l,T2 r) 490 : a(l),b(r) 491 {} // end of Expr 492 /*! 493 * access operator 494 * \param[in] i : row index 495 * \param[in] i : column index 496 */ 497 TFEL_MATH_INLINE 498 BinaryOperationHandler<typename tfel::meta::ResultOf<lhs_storage_type,size_type>::type, 499 typename tfel::meta::ResultOf<rhs_storage_type,size_type>::type,OpMult> operator ()tfel::math::Expr500 operator()(const size_type i, 501 const size_type j) const 502 503 { 504 return (this->a(i))*(this->b(j)); 505 } 506 protected: 507 lhs_storage_type a; //<! left hand side argument of the operation 508 rhs_storage_type b; //<! right hand side argument of the operation 509 }; // end of struct Expr<ResultType,DiadicProductOperation<T1,T2,Op>> 510 511 /*! 512 * \brief evaluate an expression 513 * \return the computed value 514 * \param[in] e: expression 515 */ 516 template<typename ResultType,typename Operation> 517 ResultType eval(const Expr<ResultType,Operation>& e){ 518 return {e}; 519 } // end of eval 520 521 } // end of namespace math 522 523 } // end of namespace tfel 524 525 #endif /* LIB_TFEL_MATH_EXPRESSION_EXPR_HXX */ 526