1 /* 2 Copyright (C) 2013 Tom Bachmann 3 4 This file is part of FLINT. 5 6 FLINT is free software: you can redistribute it and/or modify it under 7 the terms of the GNU Lesser General Public License (LGPL) as published 8 by the Free Software Foundation; either version 2.1 of the License, or 9 (at your option) any later version. See <http://www.gnu.org/licenses/>. 10 */ 11 12 #ifndef CXX_EXPRESSION_H 13 #define CXX_EXPRESSION_H 14 15 // TODO 16 // * static asserts 17 18 #include <iosfwd> 19 #include <string> 20 #include <cstdio> 21 22 #include "evaluation_tools.h" 23 #include "expression_traits.h" 24 #include "mp.h" 25 #include "rules.h" 26 #include "traits.h" 27 #include "tuple.h" 28 29 namespace flint { 30 namespace detail { 31 // Helper traits used by the "expression" class, in particular the evaluate() 32 // method. This is the general (i.e. non-immediate) case, 33 // which requires actual work. 34 template<class Operation, class Expr, class Data> 35 struct evaluation_traits 36 { 37 typedef typename Expr::derived_t derived_t; 38 typedef typename mp::find_evaluation< 39 Operation, Data, false>::type rule_t; 40 typedef typename mp::find_evaluation< 41 Operation, Data, true>::type temp_rule_t; 42 typedef typename rule_t::return_t evaluation_return_t; 43 typedef evaluation_return_t evaluated_t; 44 evaluateevaluation_traits45 static evaluation_return_t evaluate(const derived_t& from) 46 { 47 evaluated_t res = 48 rules::instantiate_temporaries<derived_t, evaluated_t>::get(from); 49 evaluate_into_fresh(res, from); 50 return res; 51 } 52 53 template<class T> evaluate_intoevaluation_traits54 static void evaluate_into(T& to, const derived_t& from) 55 { 56 typedef mp::back_tuple<typename rule_t::temporaries_t> back_t; 57 typename back_t::type temps_backing = 58 mp::htuples::fill<typename back_t::type>( 59 tools::temporaries_filler(from)); 60 typename rule_t::temporaries_t temps; 61 back_t::init(temps, temps_backing, 0); 62 rule_t::doit(from._data(), temps, &to); 63 } 64 evaluate_into_freshevaluation_traits65 static void evaluate_into_fresh(evaluation_return_t& to, const derived_t& from) 66 { 67 typedef mp::back_tuple< 68 typename temp_rule_t::temporaries_t, 69 evaluation_return_t 70 > back_t; 71 typename back_t::type temps_backing = 72 mp::htuples::fill<typename back_t::type>( 73 tools::temporaries_filler(from)); 74 typename temp_rule_t::temporaries_t temps; 75 back_t::init(temps, temps_backing, &to); 76 temp_rule_t::doit(from._data(), temps, &to); 77 } 78 }; 79 80 // This is the special case of an immediate argument, where "evaluation" is 81 // at most assignment. 82 template<class Expr, class Data> 83 struct evaluation_traits<operations::immediate, Expr, Data> 84 { 85 typedef typename Expr::derived_t derived_t; 86 typedef typename Expr::derived_t evaluated_t; 87 typedef evaluated_t& evaluation_return_t; 88 89 static evaluated_t& evaluate(derived_t& d) {return d;} 90 static const evaluated_t& evaluate(const derived_t& d) {return d;} 91 92 template<class T> 93 static void evaluate_into(T& to, const derived_t& from) 94 { 95 rules::assignment<T, derived_t>::doit(to, from); 96 } 97 98 static void evaluate_into_fresh(derived_t& to, const derived_t& from) 99 { 100 evaluate_into(to, from); 101 } 102 }; 103 } // detail 104 105 // The main expression template class. 106 // 107 // The argument Derived must have the following form: 108 // struct derived 109 // { 110 // template<class Operation, class Data> 111 // struct type 112 // { 113 // typedef XYZ result; 114 // }; 115 // }; 116 // See derived_wrapper below for a common example. 117 // 118 // Note that, while Data does not have to be default constructible, 119 // it *does* need to be copy-constructible, and have a working destructor. 120 template<class Derived, class Operation, class Data> 121 class expression 122 { 123 private: 124 Data data; 125 126 protected: 127 explicit expression(const Data& d) : data(d) {} 128 129 public: 130 // internal -- see is_expression implementation. 131 typedef void IS_EXPRESSION_MARKER; 132 133 typedef detail::evaluation_traits<Operation, expression, Data> ev_traits_t; 134 typedef typename Derived::template type<Operation, Data>::result derived_t; 135 typedef typename ev_traits_t::evaluated_t evaluated_t; 136 typedef typename ev_traits_t::evaluation_return_t evaluation_return_t; 137 typedef Data data_t; 138 typedef Operation operation_t; 139 140 private: 141 derived_t& downcast() {return *static_cast<derived_t*>(this);} 142 const derived_t& downcast() const 143 { 144 return *static_cast<const derived_t*>(this); 145 } 146 147 // Some helpers for initialization, since it is not possible to 148 // conditionally enable constructors in C++98 149 template<class T> 150 static data_t get_data(const T& t, 151 typename mp::disable_if<traits::is_lazy_expr<T> >::type* = 0) 152 { 153 return data_t(t); 154 } 155 template<class T> 156 static data_t get_data(T& t, 157 typename mp::disable_if<traits::is_lazy_expr<T> >::type* = 0) 158 { 159 return data_t(t); 160 } 161 template<class T> 162 static data_t get_data(const T& t, 163 typename mp::enable_if<traits::is_lazy_expr<T> >::type* = 0, 164 typename mp::disable_if< 165 mp::equal_types<typename T::evaluated_t, derived_t> >::type* = 0) 166 { 167 return data_t(t.evaluate()); 168 } 169 template<class T> 170 static data_t get_data(const T& t, 171 typename mp::enable_if<traits::is_lazy_expr<T> >::type* = 0, 172 typename mp::enable_if< 173 mp::equal_types<typename T::evaluated_t, derived_t> >::type* = 0) 174 { 175 return data_t(t.evaluate()._data()); 176 } 177 178 // Invoke the data copy constructor when appropriate 179 static data_t get_data(const derived_t& o) 180 { 181 return data_t(o._data()); 182 } 183 static data_t get_data(derived_t& o) 184 { 185 return data_t(o._data()); 186 } 187 188 // Having the empty constructor here delays its instantiation, and allows 189 // compiling even if data is *not* default constructible. 190 static data_t get_data() {return data_t();} 191 192 public: 193 // forwarded constructors 194 template<class T> 195 explicit expression(const T& t) 196 : data(get_data(t)) {} 197 198 template<class T> 199 explicit expression(T& t) 200 : data(get_data(t)) {} 201 202 template<class T, class U> 203 expression(const T& t, const U& u) 204 : data(t, u) {} 205 template<class T, class U> 206 expression(T& t, const U& u) 207 : data(t, u) {} 208 209 template<class T, class U, class V> 210 expression(const T& t, const U& u, const V& v) 211 : data(t, u, v) {} 212 template<class T, class U, class V> 213 expression(T& t, const U& u, const V& v) 214 : data(t, u, v) {} 215 template<class T, class U, class V, class W> 216 expression(const T& t, const U& u, const V& v, const W& w) 217 : data(t, u, v, w) {} 218 template<class T, class U, class V, class W> 219 expression(T& t, const U& u, const V& v, const W& w) 220 : data(t, u, v, w) {} 221 222 expression() : data(get_data()) {} 223 224 expression& operator=(const expression& o) 225 { 226 this->set(o.downcast()); 227 return *this; 228 } 229 230 // See rules::instantiate_temporaries for explanation. 231 evaluated_t create_temporary() const 232 { 233 return evaluated_t(); 234 } 235 236 Data& _data() {return data;} 237 const Data& _data() const {return data;} 238 239 void print(std::ostream& o) const 240 { 241 tools::print_using_str<evaluated_t>::doit(evaluate(), o); 242 } 243 244 std::string to_string(int base = 10) const 245 { 246 return rules::to_string<evaluated_t>::get(evaluate(), base); 247 } 248 249 template<class T> 250 T to() const 251 { 252 return rules::conversion<T, evaluated_t>::get(evaluate()); 253 } 254 255 int print(FILE* f = stdout) const 256 { 257 return rules::cprint<evaluated_t>::doit(f, evaluate()); 258 } 259 int print_pretty(FILE* f = stdout) const 260 { 261 return rules::print_pretty<evaluated_t>::doit(f, evaluate()); 262 } 263 template<class T> 264 int print_pretty(const T& extra, FILE* f = stdout) const 265 { 266 return rules::print_pretty<evaluated_t>::doit(f, evaluate(), extra); 267 } 268 int read(FILE* f = stdin) 269 { 270 return rules::read<derived_t>::doit(f, downcast()); 271 } 272 273 typename traits::make_const<evaluation_return_t>::type evaluate() const 274 { 275 return ev_traits_t::evaluate(downcast()); 276 } 277 evaluation_return_t evaluate() {return ev_traits_t::evaluate(downcast());} 278 279 template<class T> 280 void set(const T& t, 281 typename mp::enable_if<traits::is_expression<T> >::type* = 0, 282 typename mp::enable_if<traits::can_evaluate_into< 283 derived_t, typename T::evaluated_t> >::type* = 0) 284 { 285 T::ev_traits_t::evaluate_into(downcast(), t); 286 } 287 template<class T> 288 void set(const T& t, 289 typename mp::enable_if<traits::is_expression<T> >::type* = 0, 290 typename mp::disable_if<traits::can_evaluate_into< 291 derived_t, typename T::evaluated_t> >::type* = 0) 292 { 293 rules::assignment<derived_t, typename T::evaluated_t>::doit( 294 downcast(), t.evaluate()); 295 } 296 template<class T> 297 void set(const T& t, 298 typename mp::disable_if<traits::is_expression<T> >::type* = 0) 299 { 300 rules::assignment<derived_t, T>::doit(downcast(), t); 301 } 302 303 template<class T> 304 bool equals(const T& t, 305 typename mp::enable_if<traits::is_lazy_expr<T> >::type* = 0) const 306 { 307 return equals(t.evaluate()); 308 } 309 template<class T> 310 bool equals(const T& t, 311 typename mp::disable_if<traits::is_lazy_expr<T> >::type* = 0) const 312 { 313 return tools::equals_using_cmp<evaluated_t, T>::get(evaluate(), t); 314 } 315 316 template<class Op, class NData> 317 struct make_helper 318 { 319 typedef typename Derived::template type<Op, NData>::result type; 320 static type make(const NData& ndata) 321 { 322 return type(ndata); 323 } 324 }; 325 }; 326 327 // If your expression template is of the form 328 // template<class Operation, class Data> 329 // class my_expression ... 330 // then derived_wrapper<my_expression> is a valid argument for Derived in 331 // the expression class above. 332 template<template<class O, class D> class Derived> 333 struct derived_wrapper 334 { 335 template<class Operation, class Data> 336 struct type 337 { 338 typedef Derived<Operation, Data> result; 339 }; 340 }; 341 342 // If your expression template is of the form 343 // template<class Extra, class Opeartion, class Data> 344 // class my_expression2 ... 345 // where Extra is some extra information which should be passed on unchanged, 346 // then derived_wrapper2<my_expression2, Extra> is a valid argument for Derived 347 // in the expression class above. 348 template<template<class E, class O, class D> class Derived, class Extra> 349 struct derived_wrapper2 350 { 351 template<class Operation, class Data> 352 struct type 353 { 354 typedef Derived<Extra, Operation, Data> result; 355 }; 356 }; 357 358 359 // operators 360 361 namespace detail { 362 // These traits determine how arguments of an expression template are stored. 363 // E.g. (e1 + e2) yields a new expression template with a two-argument tuple 364 // as Data. If e1 is an immediate, then we want to (usually) store it by 365 // reference, to avoid copies. If not, we can just store by value (since 366 // copying e1 just copies the references anyway) and avoid indirection. 367 // (Similarly for e2.) 368 template<class Expr> 369 struct storage_traits 370 : mp::if_< 371 traits::is_immediate<Expr>, 372 typename traits::forwarding<Expr>::type, 373 Expr 374 > { }; 375 // See tuple.h. 376 template<> 377 struct storage_traits<detail::UNUSED> {typedef detail::UNUSED type;}; 378 379 template<class ev_t, class Op, class type> 380 struct nary_op_helper_step2 381 { 382 typedef typename ev_t::return_t Expr; 383 typedef typename Expr::template make_helper<Op, type> make_helper; 384 typedef typename make_helper::type return_t; 385 }; 386 template<class Op, class type> 387 struct nary_op_helper_step2<rules::UNIMPLEMENTED, Op, type> 388 { 389 struct return_t { }; 390 struct make_helper { }; 391 }; 392 393 // Helper to determine the return type of an expression, where Data is already 394 // the correct tuple type. 395 // The step1/step2 splitting above is necessary to avoid compiler errors in 396 // case there is not actually any rule. 397 template<class Op, class Data> 398 struct nary_op_helper 399 { 400 typedef typename mp::find_evaluation<Op, Data, true>::type ev_t; 401 typedef nary_op_helper_step2<ev_t, Op, Data> nohs2; 402 typedef typename nohs2::return_t return_t; 403 typedef typename nohs2::make_helper make_helper; 404 405 typedef traits::is_implemented<ev_t> cond; 406 typedef mp::enable_if<cond, return_t> enable; 407 }; 408 409 template<class Op, class Maker> 410 struct nary_op_helper_maker 411 : nary_op_helper<Op, typename Maker::type> 412 { 413 typedef Maker maker; 414 }; 415 416 #define FLINTXX_NARY_OP_HELPER_MACRO(arg) typename storage_traits< arg >::type 417 418 // nary_op_helper<Op, Arg1, Arg2, ...> invokes nary_op_helper with the correct 419 // tuple type as argument. 420 template<class Op, FLINTXX_MAKE_TUPLE_TEMPLATE_ARGS> 421 struct nary_op_helper2 422 : nary_op_helper_maker<Op, mp::make_tuple< 423 FLINTXX_MAKE_TUPLE_TYPES_APPLYMACRO(FLINTXX_NARY_OP_HELPER_MACRO) > > 424 { 425 typedef nary_op_helper2 noh2; 426 static typename noh2::return_t make(FLINTXX_MAKE_TUPLE_FUNC_ARGS) 427 { 428 return noh2::make_helper::make(noh2::maker::make( 429 FLINTXX_MAKE_TUPLE_FUNC_ARG_NAMES)); 430 } 431 }; 432 433 // Special casing for binary operators. 434 template<class Expr1, class Op, class Expr2> 435 struct binary_op_helper 436 : nary_op_helper2<Op, Expr1, Expr2> 437 { }; 438 439 // Special casing for unary operations. 440 template<class Op, class Expr> 441 struct unary_op_helper : nary_op_helper2<Op, Expr> { }; 442 443 // For unary member operators, determining the return type the normal way can 444 // lead to cyclic dependencies. See FLINTXX_DEFINE_MEMBER_UNOP_RTYPE. 445 template<class Ret, class Op, class Expr> 446 struct unary_op_helper_with_rettype 447 { 448 typedef mp::make_tuple<typename storage_traits<Expr>::type> maker; 449 typedef typename Ret::template make_helper< 450 Op, typename maker::type>::type return_t; 451 }; 452 453 // Common helper for implementing comparison operators. 454 template<class Expr1, class Expr2> 455 struct order_op_helper 456 { 457 typedef typename tools::evaluation_helper<Expr1>::type ev1_t; 458 typedef typename tools::evaluation_helper<Expr2>::type ev2_t; 459 typedef tools::symmetric_cmp<ev1_t, ev2_t> scmp; 460 461 typedef mp::enable_if< 462 mp::and_< 463 traits::is_implemented<scmp>, 464 mp::or_< 465 traits::is_expression<Expr1>, 466 traits::is_expression<Expr2> 467 > 468 >, 469 bool> enable; 470 471 static int get(const Expr1& e1, const Expr2& e2) 472 { 473 return scmp::get(tools::evaluation_helper<Expr1>::get(e1), 474 tools::evaluation_helper<Expr2>::get(e2)); 475 } 476 }; 477 } // detail 478 479 template<class Expr> 480 inline typename mp::enable_if<traits::is_expression<Expr>, std::ostream&>::type 481 operator<<(std::ostream& o, const Expr& e) 482 { 483 e.print(o); 484 return o; 485 } 486 487 template<class Expr1, class Expr2> 488 inline typename mp::enable_if<traits::is_expression<Expr1>, bool>::type 489 operator==(const Expr1& e1, const Expr2& e2) 490 { 491 return e1.equals(e2); 492 } 493 494 template<class Expr1, class Expr2> 495 inline typename mp::enable_if<mp::and_< 496 mp::not_<traits::is_expression<Expr1> >, 497 traits::is_expression<Expr2> >, 498 bool>::type 499 operator==(const Expr1& e1, const Expr2& e2) 500 { 501 return e2.equals(e1); 502 } 503 504 template<class Expr1, class Expr2> 505 inline typename mp::enable_if<mp::or_< 506 traits::is_expression<Expr1>, 507 traits::is_expression<Expr2> >, 508 bool>::type 509 operator!=(const Expr1& e1, const Expr2& e2) 510 { 511 return !(e1 == e2); 512 } 513 514 template<class Expr1, class Expr2> 515 inline typename detail::order_op_helper<Expr1, Expr2>::enable::type 516 operator<(const Expr1& e1, const Expr2& e2) 517 { 518 return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) < 0; 519 } 520 521 template<class Expr1, class Expr2> 522 inline typename detail::order_op_helper<Expr1, Expr2>::enable::type 523 operator<=(const Expr1& e1, const Expr2& e2) 524 { 525 return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) <= 0; 526 } 527 528 template<class Expr1, class Expr2> 529 inline typename detail::order_op_helper<Expr1, Expr2>::enable::type 530 operator>(const Expr1& e1, const Expr2& e2) 531 { 532 return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) > 0; 533 } 534 535 template<class Expr1, class Expr2> 536 inline typename detail::order_op_helper<Expr1, Expr2>::enable::type 537 operator>=(const Expr1& e1, const Expr2& e2) 538 { 539 return detail::order_op_helper<Expr1, Expr2>::get(e1, e2) >= 0; 540 } 541 542 template<class Expr1, class Expr2> 543 inline typename detail::binary_op_helper< 544 Expr1, operations::plus, Expr2>::enable::type 545 operator+(const Expr1& e1, const Expr2& e2) 546 { 547 return detail::binary_op_helper<Expr1, operations::plus, Expr2>::make(e1, e2); 548 } 549 550 template<class Expr1, class Expr2> 551 inline typename detail::binary_op_helper< 552 Expr1, operations::minus, Expr2>::enable::type 553 operator-(const Expr1& e1, const Expr2& e2) 554 { 555 return detail::binary_op_helper<Expr1, operations::minus, Expr2>::make(e1, e2); 556 } 557 558 template<class Expr1, class Expr2> 559 inline typename detail::binary_op_helper< 560 Expr1, operations::times, Expr2>::enable::type 561 operator*(const Expr1& e1, const Expr2& e2) 562 { 563 return detail::binary_op_helper<Expr1, operations::times, Expr2>::make(e1, e2); 564 } 565 566 template<class Expr1, class Expr2> 567 inline typename detail::binary_op_helper< 568 Expr1, operations::divided_by, Expr2>::enable::type 569 operator/(const Expr1& e1, const Expr2& e2) 570 { 571 return detail::binary_op_helper<Expr1, operations::divided_by, Expr2>::make(e1, e2); 572 } 573 574 template<class Expr1, class Expr2> 575 inline typename detail::binary_op_helper< 576 Expr1, operations::modulo, Expr2>::enable::type 577 operator%(const Expr1& e1, const Expr2& e2) 578 { 579 return detail::binary_op_helper<Expr1, operations::modulo, Expr2>::make(e1, e2); 580 } 581 582 template<class Expr1, class Expr2> 583 inline typename detail::binary_op_helper< 584 Expr1, operations::binary_and, Expr2>::enable::type 585 operator&(const Expr1& e1, const Expr2& e2) 586 { 587 return detail::binary_op_helper<Expr1, operations::binary_and, Expr2>::make(e1, e2); 588 } 589 590 template<class Expr1, class Expr2> 591 inline typename detail::binary_op_helper< 592 Expr1, operations::binary_or, Expr2>::enable::type 593 operator|(const Expr1& e1, const Expr2& e2) 594 { 595 return detail::binary_op_helper<Expr1, operations::binary_or, Expr2>::make(e1, e2); 596 } 597 598 template<class Expr1, class Expr2> 599 inline typename detail::binary_op_helper< 600 Expr1, operations::binary_xor, Expr2>::enable::type 601 operator^(const Expr1& e1, const Expr2& e2) 602 { 603 return detail::binary_op_helper<Expr1, operations::binary_xor, Expr2>::make(e1, e2); 604 } 605 606 template<class Expr1, class Expr2> 607 inline typename detail::binary_op_helper< 608 Expr1, operations::shift, Expr2>::enable::type 609 operator<<(const Expr1& e1, const Expr2& e2) 610 { 611 return detail::binary_op_helper<Expr1, operations::shift, Expr2>::make(e1, e2); 612 } 613 614 template<class Expr1, class Expr2> 615 inline typename detail::binary_op_helper< 616 Expr1, operations::shift, Expr2>::enable::type 617 operator>>(const Expr1& e1, const Expr2& e2) 618 { 619 return detail::binary_op_helper<Expr1, operations::shift, Expr2>::make(e1, -e2); 620 } 621 622 template<class Expr> 623 inline typename detail::unary_op_helper<operations::negate, Expr>::enable::type 624 operator-(const Expr& e) 625 { 626 return detail::unary_op_helper<operations::negate, Expr>::make(e); 627 } 628 629 template<class Expr> 630 inline typename detail::unary_op_helper<operations::complement, Expr>::enable::type 631 operator~(const Expr& e) 632 { 633 return detail::unary_op_helper<operations::complement, Expr>::make(e); 634 } 635 636 template<class Expr1, class Expr2> 637 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 638 operator+=(Expr1& e1, const Expr2& e2) 639 { 640 e1.set(e1 + e2); 641 return e1; 642 } 643 644 template<class Expr1, class Expr2> 645 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 646 operator-=(Expr1& e1, const Expr2& e2) 647 { 648 e1.set(e1 - e2); 649 return e1; 650 } 651 652 template<class Expr1, class Expr2> 653 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 654 operator*=(Expr1& e1, const Expr2& e2) 655 { 656 e1.set(e1 * e2); 657 return e1; 658 } 659 660 template<class Expr1, class Expr2> 661 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 662 operator/=(Expr1& e1, const Expr2& e2) 663 { 664 e1.set(e1 / e2); 665 return e1; 666 } 667 668 template<class Expr1, class Expr2> 669 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 670 operator%=(Expr1& e1, const Expr2& e2) 671 { 672 e1.set(e1 % e2); 673 return e1; 674 } 675 676 template<class Expr1, class Expr2> 677 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 678 operator|=(Expr1& e1, const Expr2& e2) 679 { 680 e1.set(e1 | e2); 681 return e1; 682 } 683 684 template<class Expr1, class Expr2> 685 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 686 operator&=(Expr1& e1, const Expr2& e2) 687 { 688 e1.set(e1 & e2); 689 return e1; 690 } 691 692 template<class Expr1, class Expr2> 693 inline typename mp::enable_if<traits::is_immediate_expr<Expr1>, Expr1&>::type 694 operator^=(Expr1& e1, const Expr2& e2) 695 { 696 e1.set(e1 ^ e2); 697 return e1; 698 } 699 700 // c-style IO 701 template<class T> 702 typename mp::enable_if<traits::is_implemented< 703 rules::cprint<typename T::evaluated_t> >, int>::type 704 print(const T& t) 705 { 706 return t.print(); 707 } 708 template<class T> 709 typename mp::enable_if<traits::is_implemented< 710 rules::cprint<typename T::evaluated_t> >, int>::type 711 print(FILE* f, const T& t) 712 { 713 return t.print(f); 714 } 715 template<class T, class U> 716 typename mp::enable_if<traits::is_implemented< 717 rules::print_pretty<typename T::evaluated_t> >, int>::type 718 print_pretty(const T& t, const U& extra) 719 { 720 return t.print_pretty(extra); 721 } 722 template<class T, class U> 723 typename mp::enable_if<traits::is_implemented< 724 rules::print_pretty<typename T::evaluated_t> >, int>::type 725 print_pretty(FILE* f, const T& t, const U& extra) 726 { 727 return t.print_pretty(extra, f); 728 } 729 template<class T> 730 typename mp::enable_if<traits::is_implemented< 731 rules::print_pretty<typename T::evaluated_t> >, int>::type 732 print_pretty(const T& t) 733 { 734 return t.print_pretty(); 735 } 736 template<class T> 737 typename mp::enable_if<traits::is_implemented< 738 rules::print_pretty<typename T::evaluated_t> >, int>::type 739 print_pretty(FILE* f, const T& t) 740 { 741 return t.print_pretty(f); 742 } 743 template<class T> 744 typename mp::enable_if<traits::is_implemented< 745 rules::read<typename T::evaluated_t> >, int>::type 746 read(T& t) 747 { 748 return t.read(); 749 } 750 template<class T> 751 typename mp::enable_if<traits::is_implemented< 752 rules::read<typename T::evaluated_t> >, int>::type 753 read(FILE* f, T& t) 754 { 755 return t.read(f); 756 } 757 758 // TODO move to std? 759 template<class Expr1, class Expr2> 760 inline typename mp::enable_if<typename traits::is_implemented< 761 rules::swap<Expr1, Expr2> > >::type swap(Expr1& e1, Expr2& e2) 762 { 763 rules::swap<Expr1, Expr2>::doit(e1, e2); 764 } 765 } 766 767 // TODO remove this? 768 #include "default_rules.h" 769 770 771 //////////////////////////////////////////////////////////////////////// 772 // HELPER MACROS 773 //////////////////////////////////////////////////////////////////////// 774 775 // To be called in any namespace 776 777 // Make the binary operation "name" available in current namespace 778 #define FLINT_DEFINE_BINOP_HERE(name) \ 779 template<class T1, class T2> \ 780 inline typename ::flint::detail::binary_op_helper<\ 781 T1, ::flint::operations::name##_op, T2>::enable::type \ 782 name(const T1& t1, const T2& t2) \ 783 { \ 784 return ::flint::detail::binary_op_helper< \ 785 T1, ::flint::operations::name##_op, T2>::make(t1, t2); \ 786 } 787 788 // Make the unary operation "name" available in current namespace 789 #define FLINT_DEFINE_UNOP_HERE(name) \ 790 template<class T1> \ 791 inline typename ::flint::detail::unary_op_helper<\ 792 ::flint::operations::name##_op, T1>::enable::type \ 793 name(const T1& t1) \ 794 { \ 795 return ::flint::detail::unary_op_helper< ::flint::operations::name##_op, T1>::make(t1); \ 796 } 797 798 // Make the threeary operation "name" available in current namespace 799 #define FLINT_DEFINE_THREEARY_HERE(name) \ 800 template<class T1, class T2, class T3> \ 801 inline typename ::flint::detail::nary_op_helper2<\ 802 ::flint::operations::name##_op, T1, T2, T3>::enable::type \ 803 name(const T1& t1, const T2& t2, const T3& t3) \ 804 { \ 805 return ::flint::detail::nary_op_helper2< \ 806 ::flint::operations::name##_op, T1, T2, T3>::make(t1, t2, t3); \ 807 } 808 809 // Make the threeary operation "name" available in current namespace, 810 // but with only two arguments, the second of which is of type type1 and 811 // defaults to val1, and the third argument always (implicitly) of type type2 812 // and value val2. 813 // The suggested usage of this macro is to first call FLINT_DEFINE_THREEARY_HERE, 814 // and then call FLINT_DEFINE_THREEARY_HERE_2DEFAULT. The effect will be an 815 // operation which can be invoked with 1, 2 or 3 arguments. 816 #define FLINT_DEFINE_THREEARY_HERE_2DEFAULT(name, type1, val1, type2, val2) \ 817 template<class T1> \ 818 inline typename ::flint::detail::nary_op_helper2<\ 819 ::flint::operations::name##_op, T1, type1, type2 >::enable::type \ 820 name(const T1& t1, type1 t2 = val1) \ 821 { \ 822 return ::flint::detail::nary_op_helper2< \ 823 ::flint::operations::name##_op, T1, type1, type2>::make(t1, t2, val2); \ 824 } 825 826 // Make the fourary operation "name" available in current namespace 827 #define FLINT_DEFINE_FOURARY_HERE(name) \ 828 template<class T1, class T2, class T3, class T4> \ 829 inline typename ::flint::detail::nary_op_helper2<\ 830 ::flint::operations::name##_op, T1, T2, T3, T4>::enable::type \ 831 name(const T1& t1, const T2& t2, const T3& t3, const T4& t4) \ 832 { \ 833 return ::flint::detail::nary_op_helper2< \ 834 ::flint::operations::name##_op, T1, T2, T3, T4>::make(t1, t2, t3, t4); \ 835 } 836 837 // Make the fiveary operation "name" available in current namespace 838 #define FLINT_DEFINE_FIVEARY_HERE(name) \ 839 template<class T1, class T2, class T3, class T4, class T5> \ 840 inline typename ::flint::detail::nary_op_helper2<\ 841 ::flint::operations::name##_op, T1, T2, T3, T4, T5>::enable::type \ 842 name(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) \ 843 { \ 844 return ::flint::detail::nary_op_helper2< \ 845 ::flint::operations::name##_op, T1, T2, T3, T4, T5>::make(t1, t2, t3, t4, t5); \ 846 } 847 848 // Make the sixary operation "name" available in current namespace 849 #define FLINT_DEFINE_SIXARY_HERE(name) \ 850 template<class T1, class T2, class T3, class T4, class T5, class T6> \ 851 inline typename ::flint::detail::nary_op_helper2<\ 852 ::flint::operations::name##_op, T1, T2, T3, T4, T5, T6>::enable::type \ 853 name(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6) \ 854 { \ 855 return ::flint::detail::nary_op_helper2< \ 856 ::flint::operations::name##_op, T1, T2, T3, T4, T5, T6>::make(t1, t2, t3, t4, t5, t6); \ 857 } 858 859 // Make the sevenary operation "name" available in current namespace 860 #define FLINT_DEFINE_SEVENARY_HERE(name) \ 861 template<class T1, class T2, class T3, class T4, class T5, class T6, class T7> \ 862 inline typename ::flint::detail::nary_op_helper2<\ 863 ::flint::operations::name##_op, T1, T2, T3, T4, T5, T6, T7>::enable::type \ 864 name(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7) \ 865 { \ 866 return ::flint::detail::nary_op_helper2< \ 867 ::flint::operations::name##_op, T1, T2, T3, T4, T5, T6, T7>::make(t1, t2, t3, t4, t5, t6, t7); \ 868 } 869 870 // This set of macros should be called in namespace flint. 871 872 // Introduce a new binary operation called "name" 873 // NB: because of ADL bugs in g++ <= 4.4, the operation tag is called "name_op", 874 // whereas the function corresponding to it is just called "name" 875 #define FLINT_DEFINE_BINOP(name) \ 876 namespace operations { \ 877 struct name##_op { }; \ 878 } \ 879 FLINT_DEFINE_BINOP_HERE(name) 880 881 // This macro can be used to conditionally enable a function, and is mostly 882 // used for forwarding. 883 // A typical usage is 884 // template<class T, class U> 885 // FLINT_BINOP_ENABLE_RETTYPE(myop, T, U) myop_other(const T& t, const U& u) 886 // { 887 // // perhaps something more interesting 888 // return myop(t, u); 889 // } 890 #define FLINT_BINOP_ENABLE_RETTYPE(name, T1, T2) \ 891 typename detail::binary_op_helper<T1, operations::name##_op, T2>::enable::type 892 893 // Introduce a new unary operation called "name" 894 #define FLINT_DEFINE_UNOP(name) \ 895 namespace operations { \ 896 struct name##_op { }; \ 897 } \ 898 FLINT_DEFINE_UNOP_HERE(name) 899 900 #define FLINT_UNOP_ENABLE_RETTYPE(name, T) \ 901 typename detail::unary_op_helper<operations::name##_op, T>::return_t 902 903 // See FLINTXX_DEFINE_MEMBER_UNOP_RTYPE 904 #define FLINT_UNOP_BUILD_RETTYPE(name, rettype, T) \ 905 typename detail::unary_op_helper_with_rettype<rettype, \ 906 operations::name##_op, T>::return_t 907 908 #define FLINT_DEFINE_THREEARY(name) \ 909 namespace operations { \ 910 struct name##_op { }; \ 911 } \ 912 FLINT_DEFINE_THREEARY_HERE(name) 913 914 #define FLINT_THREEARY_ENABLE_RETTYPE(name, T1, T2, T3) \ 915 typename detail::nary_op_helper2<operations::name##_op, T1, T2, T3>::enable::type 916 917 #define FLINT_DEFINE_FOURARY(name) \ 918 namespace operations { \ 919 struct name##_op { }; \ 920 } \ 921 FLINT_DEFINE_FOURARY_HERE(name) 922 923 #define FLINT_FOURARY_ENABLE_RETTYPE(name, T1, T2, T3, T4) \ 924 typename detail::nary_op_helper2<operations::name##_op, T1, T2, T3, T4>::enable::type 925 926 #define FLINT_DEFINE_FIVEARY(name) \ 927 namespace operations { \ 928 struct name##_op { }; \ 929 } \ 930 FLINT_DEFINE_FIVEARY_HERE(name) 931 932 #define FLINT_FIVEARY_ENABLE_RETTYPE(name, T1, T2, T3, T4, T5) \ 933 typename detail::nary_op_helper2<operations::name##_op, T1, T2, T3, T4, T5>::enable::type 934 935 #define FLINT_DEFINE_SIXARY(name) \ 936 namespace operations { \ 937 struct name##_op { }; \ 938 } \ 939 FLINT_DEFINE_SIXARY_HERE(name) 940 941 #define FLINT_DEFINE_SEVENARY(name) \ 942 namespace operations { \ 943 struct name##_op { }; \ 944 } \ 945 FLINT_DEFINE_SEVENARY_HERE(name) 946 947 #endif 948