Boost logo

Boost :

From: Maurizio Vitale (mav_at_[hidden])
Date: 2007-04-17 10:42:54


Good day,

The following example conceptually builds on the previous one I've
posted (but it is self contained). It requires boost::proto from CVS
head, as Eric has fixed one or two bugs. Thanks, Eric!

What it does is to define two user defined types, my_int wrapping
ints, and my_uint wrapping unsigned ints.

It then defines two grammars, my_grammar which allows arbitrary mix of
signed and unsigned quantities and my_segregated_grammar which doesn't
allow those incestuous relations.

Furthemore, the example does allow to modify the normal C++ rule for
when signed and unsigned are mixed together: define priority to be
magnitude_c and you get the standard (and not particularly useful,
imho, C/C++ rule). Define priority to be two_complement_c and you get
the rule I really need for SystemC: if signed is combined with
unsigned, signed win.

Look in the code for the section CONFIGURATION for changing the
settings. Right now the settings are global, I'm investigating how to
propagate information from the LHS into the RHS in order to allow the
LHS to define the behaviour (not particularly meaningful in this case,
but I'll need the same capabilty for other more interesting things).

The code is public domain. If anybody see something bad in the code,
let me know. I've noticed myself the lack of comments, no need to
mention it :-)

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/xpressive/proto/transform/arg.hpp>
#include <boost/xpressive/proto/transform/construct.hpp>
//#include <boost/xpressive/proto/transform/fold_to_list.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/typeof/std/ostream.hpp>

#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/vector.hpp>

#include <boost/test/unit_test.hpp>

namespace proto=boost::proto;
namespace mpl=boost::mpl;

enum binary_representation_enum {magnitude, two_complement};
typedef mpl::integral_c<binary_representation_enum, magnitude> magnitude_c;
typedef mpl::integral_c<binary_representation_enum, two_complement> two_complement_c;

//............... CONFIGURATION ..............................................................................
//typedef magnitude_c priority; // default C++ mode, expressions containing unsigned are unsigned
typedef two_complement_c priority; // expression contained signed are signed

struct my_domain : proto::domain<struct my_grammar> {}; // accept mix of signed/unsigned
//struct my_domain : proto::domain<struct my_segregated_grammar> {}; // signed and unsigned cannot mix
//............... Configuration ..............................................................................

template<typename> struct my_context;

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 =;

  operator int () const {
    return static_cast<int>(proto::eval(*this, my_context<Expr> ()));
  }

  operator unsigned int () const {
    return static_cast<unsigned int>(proto::eval(*this, my_context<Expr> ()));
  }
};

template<typename Grammar, typename Priority>
struct binary_return_type : Grammar {

  template<typename T> struct other : mpl::if_<boost::is_same<T, magnitude_c>, two_complement_c, magnitude_c> {};

  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 left_type;
    typedef typename proto::result_of::right<expr_type>::type right_type;
    typedef typename mpl::if_<mpl::contains<mpl::vector<left_type, right_type>, Priority>, Priority, typename other<Priority>::type>::type type;
  };
  
  template<typename Expr, typename State, typename Visitor>
  static typename apply<Expr, State, Visitor>::type
  call (const Expr&, const State&, Visitor&) {
    return typename apply<Expr, State, Visitor>::type ();
  }
};

template<typename, typename> struct number;

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

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

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

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

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

template<typename Priority>
struct return_type_grammar : proto::or_ <
  proto::trans::always<proto::terminal<number<proto::_,magnitude_c> >, magnitude_c>,
  proto::trans::always<proto::terminal<number<proto::_,two_complement_c> >, two_complement_c>,
  proto::trans::always<proto::terminal<int >, two_complement_c>,
  proto::trans::always<proto::terminal<unsigned int >, magnitude_c>,

  proto::trans::arg<proto::unary_expr<proto::_, return_type_grammar<Priority> > >,
  binary_return_type<proto::binary_expr<proto::_, return_type_grammar<Priority>, return_type_grammar<Priority> >, Priority >
>
{};

struct my_grammar : proto::or_ <
  proto::terminal<number<proto::_,two_complement_c> > ,
  proto::terminal<number<proto::_,magnitude_c> >,
  proto::unary_expr<proto::_, my_grammar> ,
  proto::binary_expr<proto::_, my_grammar, my_grammar>
>
{};

struct my_unsigned_grammar : proto::or_ <
  proto::terminal<number<proto::_,magnitude_c> >,
  proto::terminal<unsigned int>,
  proto::unary_expr<proto::_, my_unsigned_grammar> ,
  proto::binary_expr<proto::_, my_unsigned_grammar, my_unsigned_grammar>
>
{};

struct my_signed_grammar : proto::or_ <
  proto::terminal<number<proto::_,two_complement_c> > ,
  proto::terminal<int>,
  proto::unary_expr<proto::_, my_signed_grammar> ,
  proto::binary_expr<proto::_, my_signed_grammar, my_signed_grammar>
>
{};

struct my_segregated_grammar : proto::or_ <
  my_signed_grammar,
  my_unsigned_grammar
> {};

template<typename Expr, typename Priority>
struct return_type : return_type_grammar<Priority>::template apply<Expr, magnitude_c, mpl::void_>
{};

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 Expr>
struct my_context : proto::callable_context<const my_context<Expr> > {

  typedef typename mpl::if_<boost::is_same<typename return_type<Expr, ::priority>::type, magnitude_c>,
                            unsigned int,
                            int>::type result_type;

  template<typename T>
  unsigned int operator () (proto::tag::terminal, number<T, magnitude_c> n) const { return n.m_data; }

  template<typename T>
  int operator () (proto::tag::terminal, number<T, two_complement_c> n) const { return n.m_data; }
};

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

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

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

template<int N>
struct my_uint : my_expr<typename proto::terminal<number<mpl::int_<N>, magnitude_c > >::type>
{
  typedef my_expr<typename proto::terminal<number<mpl::int_<N>, magnitude_c > >::type> expr_type;

  my_uint () {}
  my_uint (int i) : expr_type (expr_type::type::make (i)) {}

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

#include <boost/test/included/unit_test_framework.hpp>
using namespace boost::unit_test;

template<typename Expr>
void test (const Expr& expr) {
  std::cout << "Expression:\n";
    display_expr (expr);
  if (proto::matches<Expr, my_grammar>::value)
    std::cout << "matches my grammar\n\n";
  else
    std::cout << "doesn't matches my grammar\n\n";
}

void misc_test () {
  my_int<6> i4(-22);
  my_uint<6> ui4(7);

  int i;
  unsigned int j;

  i = i4/ui4;
  j = i4/ui4;

  std::cout << "my_expr, -22/7 [assigned to int]=" << i << "\n";
  std::cout << "my_expr, -22/7 [assigned to unsigned int]=" << j << "\n";
}

test_suite*
init_unit_test_suite (int, char**)
{
  test_suite* test= BOOST_TEST_SUITE( "boost::proto sandbox" );

  test->add (BOOST_TEST_CASE (&misc_test));

  return test;
}


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