text: //  Copyright (c) 2001-2011 Hartmut Kaiser
  //
  //  Distributed under the Boost Software License, Version 1.0. (See accompanying
  //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  
  #if !defined(BOOST_SPIRIT_SUPPORT_META_CREATE_NOV_21_2009_0327PM)
  #define BOOST_SPIRIT_SUPPORT_META_CREATE_NOV_21_2009_0327PM
  
  #if defined(_MSC_VER)
  #pragma once
  #endif
  
  #include <boost/spirit/home/support/unused.hpp>
  
  #include <boost/version.hpp>
  #include <boost/spirit/include/phoenix_limits.hpp>      // needs to be included before proto
  #include <boost/proto/proto.hpp>
  #include <boost/utility/result_of.hpp>
  #include <boost/type_traits/add_const.hpp>
  #include <boost/type_traits/add_reference.hpp>
  #include <boost/type_traits/remove_const.hpp>
  #include <boost/type_traits/remove_reference.hpp>
  #include <boost/fusion/include/fold.hpp>
  #include <boost/mpl/and.hpp>
  #include <boost/mpl/not.hpp>
  
  // needed for workaround below
  #if defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ < 3))
  #include <boost/type_traits/is_same.hpp>
  #endif
  
  namespace boost { namespace spirit { namespace traits
  {
      ///////////////////////////////////////////////////////////////////////////
      // This is the main dispatch point for meta_create to the correct domain
      template <typename Domain, typename T, typename Enable = void>
      struct meta_create;
  
      ///////////////////////////////////////////////////////////////////////////
      // This allows to query whether a valid mapping exists for the given data
      // type to a component in the given domain
      template <typename Domain, typename T, typename Enable = void>
      struct meta_create_exists : mpl::false_ {};
  }}}
  
  namespace boost { namespace spirit
  {
      ///////////////////////////////////////////////////////////////////////////
      namespace detail
      {
          template <typename T>
          struct add_const_ref
            : add_reference<typename add_const<T>::type> {};
  
          template <typename T>
          struct remove_const_ref
            : remove_const<typename remove_reference<T>::type> {};
  
  // starting with Boost V1.42 fusion::fold has been changed to be compatible
  // with mpl::fold (the sequence of template parameters for the meta-function
  // object has been changed)
  #if BOOST_VERSION < 104200
          ///////////////////////////////////////////////////////////////////////
          template <typename OpTag, typename Domain>
          struct nary_proto_expr_function
          {
              template <typename T>
              struct result;
  
  // this is a workaround for older versions of g++ (< V4.3) which apparently have
  // problems with the following template specialization
  #if defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ < 3))
              template <typename F, typename T1, typename T2>
              struct result<F(T1, T2)>
              {
                  BOOST_STATIC_ASSERT((is_same<F, nary_proto_expr_function>::value));
  #else
              template <typename T1, typename T2>
              struct result<nary_proto_expr_function(T1, T2)>
              {
  #endif
                  typedef typename remove_const_ref<T2>::type left_type;
                  typedef typename
                      spirit::traits::meta_create<Domain, T1>::type
                  right_type;
  
                  typedef typename mpl::eval_if<
                      traits::not_is_unused<left_type>
                    , proto::result_of::make_expr<OpTag, left_type, right_type>
                    , mpl::identity<right_type>
                  >::type type;
              };
  
              template <typename T>
              typename result<nary_proto_expr_function(T, unused_type const&)>::type
              operator()(T, unused_type const&) const
              {
                  typedef spirit::traits::meta_create<Domain, T> right_type;
                  return right_type::call();
              }
  
              template <typename T1, typename T2>
              typename result<nary_proto_expr_function(T1, T2)>::type
              operator()(T1, T2 const& t2) const
              {
                  // we variants to the alternative operator
                  typedef spirit::traits::meta_create<Domain, T1> right_type;
                  return proto::make_expr<OpTag>(t2, right_type::call());
              }
          };
  #else
          ///////////////////////////////////////////////////////////////////////
          template <typename OpTag, typename Domain>
          struct nary_proto_expr_function
          {
              template <typename T>
              struct result;
  
  // this is a workaround for older versions of g++ (< V4.3) which apparently have
  // problems with the following template specialization
  #if defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ < 3))
              template <typename F, typename T1, typename T2>
              struct result<F(T1, T2)>
              {
                  BOOST_STATIC_ASSERT((is_same<F, nary_proto_expr_function>::value));
  #else
              template <typename T1, typename T2>
              struct result<nary_proto_expr_function(T1, T2)>
              {
  #endif
                  typedef typename remove_const_ref<T1>::type left_type;
                  typedef typename
                      spirit::traits::meta_create<Domain, T2>::type
                  right_type;
  
                  typedef typename mpl::eval_if<
                      traits::not_is_unused<left_type>
                    , proto::result_of::make_expr<OpTag, left_type, right_type>
                    , mpl::identity<right_type>
                  >::type type;
              };
  
              template <typename T>
              typename result<nary_proto_expr_function(unused_type const&, T)>::type
              operator()(unused_type const&, T) const
              {
                  typedef spirit::traits::meta_create<Domain, T> right_type;
                  return right_type::call();
              }
  
              template <typename T1, typename T2>
              typename result<nary_proto_expr_function(T1, T2)>::type
              operator()(T1 const& t1, T2) const
              {
                  // we variants to the alternative operator
                  typedef spirit::traits::meta_create<Domain, T2> right_type;
                  return proto::make_expr<OpTag>(t1, right_type::call());
              }
          };
  #endif
      }
  
      ///////////////////////////////////////////////////////////////////////
      template <typename T, typename OpTag, typename Domain>
      struct make_unary_proto_expr
      {
          typedef spirit::traits::meta_create<Domain, T> subject_type;
  
          typedef typename proto::result_of::make_expr<
              OpTag, typename subject_type::type
          >::type type;
  
          static type call()
          {
              return proto::make_expr<OpTag>(subject_type::call());
          }
      };
  
      ///////////////////////////////////////////////////////////////////////////
      template <typename Sequence, typename OpTag, typename Domain>
      struct make_nary_proto_expr
      {
          typedef detail::nary_proto_expr_function<OpTag, Domain>
              make_proto_expr;
  
          typedef typename fusion::result_of::fold<
              Sequence, unused_type, make_proto_expr
          >::type type;
  
          static type call()
          {
              return fusion::fold(Sequence(), unused, make_proto_expr());
          }
      };
  
      ///////////////////////////////////////////////////////////////////////////
      namespace detail
      {
          // Starting with newer versions of Proto, all Proto expressions are at
          // the same time Fusion sequences. This is the correct behavior, but
          // we need to distinguish between Fusion sequences and Proto
          // expressions. This meta-function does exactly that.
          template <typename T>
          struct is_fusion_sequence_but_not_proto_expr
            : mpl::and_<
                  fusion::traits::is_sequence<T>
                , mpl::not_<proto::is_expr<T> > >
          {};
      }
  }}
  
  #endif
  