lazy_vector.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. //[ LazyVector
  2. ///////////////////////////////////////////////////////////////////////////////
  3. // Copyright 2008 Eric Niebler. Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // This example constructs a mini-library for linear algebra, using
  8. // expression templates to eliminate the need for temporaries when
  9. // adding vectors of numbers.
  10. //
  11. // This example uses a domain with a grammar to prune the set
  12. // of overloaded operators. Only those operators that produce
  13. // valid lazy vector expressions are allowed.
  14. #include <vector>
  15. #include <iostream>
  16. #include <boost/mpl/int.hpp>
  17. #include <boost/proto/core.hpp>
  18. #include <boost/proto/context.hpp>
  19. namespace mpl = boost::mpl;
  20. namespace proto = boost::proto;
  21. using proto::_;
  22. template<typename Expr>
  23. struct lazy_vector_expr;
  24. // This grammar describes which lazy vector expressions
  25. // are allowed; namely, vector terminals and addition
  26. // and subtraction of lazy vector expressions.
  27. struct LazyVectorGrammar
  28. : proto::or_<
  29. proto::terminal< std::vector<_> >
  30. , proto::plus< LazyVectorGrammar, LazyVectorGrammar >
  31. , proto::minus< LazyVectorGrammar, LazyVectorGrammar >
  32. >
  33. {};
  34. // Tell proto that in the lazy_vector_domain, all
  35. // expressions should be wrapped in laxy_vector_expr<>
  36. // and must conform to the lazy vector grammar.
  37. struct lazy_vector_domain
  38. : proto::domain<proto::generator<lazy_vector_expr>, LazyVectorGrammar>
  39. {};
  40. // Here is an evaluation context that indexes into a lazy vector
  41. // expression, and combines the result.
  42. template<typename Size = std::size_t>
  43. struct lazy_subscript_context
  44. {
  45. lazy_subscript_context(Size subscript)
  46. : subscript_(subscript)
  47. {}
  48. // Use default_eval for all the operations ...
  49. template<typename Expr, typename Tag = typename Expr::proto_tag>
  50. struct eval
  51. : proto::default_eval<Expr, lazy_subscript_context>
  52. {};
  53. // ... except for terminals, which we index with our subscript
  54. template<typename Expr>
  55. struct eval<Expr, proto::tag::terminal>
  56. {
  57. typedef typename proto::result_of::value<Expr>::type::value_type result_type;
  58. result_type operator ()( Expr const & expr, lazy_subscript_context & ctx ) const
  59. {
  60. return proto::value( expr )[ ctx.subscript_ ];
  61. }
  62. };
  63. Size subscript_;
  64. };
  65. // Here is the domain-specific expression wrapper, which overrides
  66. // operator [] to evaluate the expression using the lazy_subscript_context.
  67. template<typename Expr>
  68. struct lazy_vector_expr
  69. : proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain>
  70. {
  71. lazy_vector_expr( Expr const & expr = Expr() )
  72. : lazy_vector_expr::proto_extends( expr )
  73. {}
  74. // Use the lazy_subscript_context<> to implement subscripting
  75. // of a lazy vector expression tree.
  76. template< typename Size >
  77. typename proto::result_of::eval< Expr, lazy_subscript_context<Size> >::type
  78. operator []( Size subscript ) const
  79. {
  80. lazy_subscript_context<Size> ctx(subscript);
  81. return proto::eval(*this, ctx);
  82. }
  83. };
  84. // Here is our lazy_vector terminal, implemented in terms of lazy_vector_expr
  85. template< typename T >
  86. struct lazy_vector
  87. : lazy_vector_expr< typename proto::terminal< std::vector<T> >::type >
  88. {
  89. typedef typename proto::terminal< std::vector<T> >::type expr_type;
  90. lazy_vector( std::size_t size = 0, T const & value = T() )
  91. : lazy_vector_expr<expr_type>( expr_type::make( std::vector<T>( size, value ) ) )
  92. {}
  93. // Here we define a += operator for lazy vector terminals that
  94. // takes a lazy vector expression and indexes it. expr[i] here
  95. // uses lazy_subscript_context<> under the covers.
  96. template< typename Expr >
  97. lazy_vector &operator += (Expr const & expr)
  98. {
  99. std::size_t size = proto::value(*this).size();
  100. for(std::size_t i = 0; i < size; ++i)
  101. {
  102. proto::value(*this)[i] += expr[i];
  103. }
  104. return *this;
  105. }
  106. };
  107. int main()
  108. {
  109. // lazy_vectors with 4 elements each.
  110. lazy_vector< double > v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 );
  111. // Add two vectors lazily and get the 2nd element.
  112. double d1 = ( v2 + v3 )[ 2 ]; // Look ma, no temporaries!
  113. std::cout << d1 << std::endl;
  114. // Subtract two vectors and add the result to a third vector.
  115. v1 += v2 - v3; // Still no temporaries!
  116. std::cout << '{' << v1[0] << ',' << v1[1]
  117. << ',' << v1[2] << ',' << v1[3] << '}' << std::endl;
  118. // This expression is disallowed because it does not conform
  119. // to the LazyVectorGrammar
  120. //(v2 + v3) += v1;
  121. return 0;
  122. }
  123. //]