Boost logo

Boost :

From: Maurizio Vitale (mav_at_[hidden])
Date: 2007-04-28 18:39:48


The code that follows is included just to illustrate an example of what I'd like to do.
It doesn't do it, as all my attempts have yielded nothing good.

In short, I have a class called my_int that defines the width at compile time by declaring
a nested type width. In this case width would be an mpl::int_<N> for some N.

I then have a class my_int_base that defines the width at construction time and contains a
data member m_width of type int. The nested type width in this case would be run_time,
just a tag.

For this example I want to compute the width of an expression as the sum of all widths.
This is just a simple example of what I need, there's no meaning for the sum of all widths.

The code attached does it for the case where all terminals have a compile-time width.

For crossing into the run-time world, I tried many variations of call. All failing.

Here's where I am. I'd appreciate hints, corrections or even full blown solutions :-).

- at the moment, apply defines type to be wither mpl::int_<N> or run_time. This has to
  change. I thought it was enough to declare a different return type for call, but it is
  not quite that easy: the fact that apply computes the return type for call is
  used inside proto.
  Let's assume width is always an integer.
  In this case should I define:

  struct apply {
         typedef int type;
         
         typedef ...somehing going to int_<N> or run_time value_type;
  };

  but if I do this, clearly proto cannot help me in computing things at
  compile-time, those rely on the value being type, not value_type.

- if the above is approximatively ok, wonderful. But now how do I define call()?
  What I'd like is of course to have as much computed at compile time, so that
  if you have an expression like R + (C1 * C2 + (C4 - C6)), where R has a runtime
  width you do exactly one addition at runtime (and with an immediate operand).

If the above is completely wrong, I'd appreciate sketches of alternative ways.
Thanks in advance and best regards,

     Maurizio

#include <iostream>
#include <boost/xpressive/proto/proto.hpp>
#include <boost/xpressive/proto/context.hpp>
#include <boost/xpressive/proto/extends.hpp>
#include <boost/xpressive/proto/debug.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/typeof/std/ostream.hpp>
#include <boost/mpl/plus.hpp>

using namespace boost;

struct run_time {};

struct my_domain : proto::domain<> {};

template <typename Expr>
struct my_expr : proto::extends<Expr, my_expr<Expr>, my_domain> {
  typedef proto::extends<Expr, my_expr<Expr>, my_domain> base_type;

  my_expr (Expr const& expr = Expr()) : base_type (expr) {};

  using base_type::operator =;

  typedef int result_type;

  operator result_type () const {
    return 10;
  }
};

namespace boost { namespace proto {
  template<typename Expr>
  struct generate<my_domain, Expr> {
    typedef my_expr<Expr> type;
      
    static type make (Expr const& expr) {
      return type (expr);
    }
  };
} } // end namespace boost::proto

template<typename T=run_time> struct number;

template<typename T>
std::ostream& operator << (std::ostream& os, const number<T> o);

template<typename T>
struct number {
  typedef T width;
  number () : m_data (0) {}
  number (int i) : m_data (i) {}
  friend std::ostream& operator << <> (std::ostream& os, const number<T> o);
  unsigned int m_data;
};

template<>
struct number<run_time> {
  typedef run_time width;
  number () : m_data (0) {}
  number (int w, int i) : m_data (i), m_width (w) {}
  friend std::ostream& operator << <> (std::ostream& os, const number<run_time> o);
  unsigned int m_data;
  unsigned int m_width;
};

template<typename T>
std::ostream& operator << (std::ostream& os, const number<T> o) { os << "~" << o.m_data << "~"; return os; }

template<typename Grammar>
struct binary_width
  : Grammar
{
  binary_width();

  template<typename Expr, typename State, typename Visitor>
  struct apply
  {
    typedef typename mpl::apply_wrap3<Grammar,Expr,State,Visitor>::type expr_type;
    typedef typename proto::result_of::left<expr_type>::type _1;
    typedef typename proto::result_of::right<expr_type>::type _2;

    typedef typename mpl::plus<_1, _2>::type type;
  };
};

template<typename Grammar>
struct terminal_width
  : Grammar
{
  terminal_width();

  template<typename Expr, typename State, typename Visitor>
  struct apply
  {
    typedef typename proto::result_of::arg_c<Expr,0>::type number;
    typedef typename number::width type;
  };
};

using proto::_;

struct width_transform : proto::or_ <
  terminal_width<proto::terminal< number<_> > >,
  binary_width<proto::binary_expr<_, width_transform, width_transform> >
> {};

template<typename Expr>
struct width : width_transform::template apply<Expr, mpl::true_, mpl::void_>
{};

template<typename Expr>
int get_width (Expr e) {
  mpl::void_ null;
  return width<Expr>::type::value;
}

template<typename Expr>
struct my_context : proto::callable_context<const my_context<Expr> > {
  typedef int result_type; // later we'll compute this from Expr
};

int foo (int,int) { return 0; }

template<int N>
struct my_int : my_expr<typename proto::terminal<number<mpl::int_<N> > >::type >
{
  typedef number<mpl::int_<N> > number_type;
  typedef my_expr<typename proto::terminal<number_type>::type> expr_type;

  my_int () {}
  my_int (int i) : expr_type (expr_type::type::make (number_type (i))) {}

  template<typename Expr>
  my_int& operator = (const Expr& e) {
    proto::arg (*this).m_data = proto::eval(e, my_context<Expr> ());
    return *this;
  }
};

struct my_int_base : my_expr<proto::terminal<number<run_time> >::type >
{
  typedef number<run_time> number_type;
  typedef my_expr<proto::terminal<number_type>::type > expr_type;

  my_int_base () {}
  my_int_base (int w, int i) : expr_type (expr_type::type::make (number_type(w,i))) {}

  template<typename Expr>
  my_int_base& operator = (const Expr& e) {
    proto::arg (*this).m_data = proto::eval(e, my_context<Expr> ());
    return *this;
  }
};

int main (int, char**) {
  my_int<4> b(3);
  my_int<8> c;

  my_int_base i(8,42);

  std::cout << "bounds(b)=" << get_width (b) << "\n";
  std::cout << "bounds(b+c)=" << get_width (b+c) << "\n";
  // std::cout << "bounds(b+i)=" << get_width (b+i) << "\n";
}


/// Local Variables:
/// mode:c++
/// comment-column:80
/// fill-column:160
/// compilation-read-command:nil
/// compile-command:"g++ -I. -oex4 ex4.cpp"
/// End:


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk