boost/spirit/home/karma/detail/alternative_function.hpp
// Copyright (c) 2001-2010 Hartmut Kaiser
// Copyright (c) 2001-2010 Joel de Guzman
//
// 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(SPIRIT_KARMA_ALTERNATIVE_MAR_01_2007_1124AM)
#define SPIRIT_KARMA_ALTERNATIVE_MAR_01_2007_1124AM
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/spirit/home/karma/domain.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/support/attributes.hpp>
#include <boost/spirit/home/support/detail/hold_any.hpp>
#include <boost/spirit/home/karma/detail/output_iterator.hpp>
#include <boost/spirit/home/support/container.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/find_if.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/distance.hpp>
#include <boost/mpl/or.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/variant.hpp>
#include <boost/detail/workaround.hpp>
///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace karma { namespace detail
{
///////////////////////////////////////////////////////////////////////////
// A component is compatible to a given Attribute type if the Attribute
// is the same as the expected type of the component
///////////////////////////////////////////////////////////////////////////
template <typename Expected, typename Attribute>
struct attribute_is_compatible
: is_convertible<Attribute, Expected> {};
template <typename Expected, typename Attribute>
struct attribute_is_compatible<Expected, boost::optional<Attribute> >
: is_convertible<Attribute, Expected> {};
template <typename Container>
struct is_hold_any_container
: is_same<hold_any, typename traits::container_value<Container>::type> {};
template <typename Expected, typename Attribute, typename IsNotVariant>
struct compute_compatible_component_variant
: mpl::or_<
attribute_is_compatible<Expected, Attribute>
, is_same<hold_any, Expected>
, mpl::eval_if<
traits::is_container<Expected>
, is_hold_any_container<Expected>
, mpl::false_>
> {};
template <typename Expected, typename Attribute>
struct compute_compatible_component_variant<Expected, Attribute, mpl::false_>
{
typedef typename traits::variant_type<Attribute>::type variant_type;
typedef typename variant_type::types types;
typedef typename mpl::end<types>::type end;
typedef typename
mpl::find_if<types, is_same<Expected, mpl::_1> >::type
iter;
typedef typename mpl::distance<
typename mpl::begin<types>::type, iter
>::type distance;
typedef typename mpl::not_<is_same<iter, end> >::type type;
enum { value = type::value };
};
template <typename Expected, typename Attribute>
struct compute_compatible_component
: compute_compatible_component_variant<Expected, Attribute
, typename spirit::traits::not_is_variant<Attribute>::type> {};
template <typename Expected>
struct compute_compatible_component<Expected, unused_type>
: mpl::false_ {};
template <typename Attribute>
struct compute_compatible_component<unused_type, Attribute>
: mpl::false_ {};
template <>
struct compute_compatible_component<unused_type, unused_type>
: mpl::false_ {};
///////////////////////////////////////////////////////////////////////////
// execute a generator if the given Attribute type is compatible
///////////////////////////////////////////////////////////////////////////
// this gets instantiated if the Attribute type is _not_ compatible with
// the generator
template <typename Component, typename Attribute, typename Expected
, typename Enable = void>
struct alternative_generate
{
template <typename OutputIterator, typename Context, typename Delimiter>
static bool
call(Component const&, OutputIterator&, Context&, Delimiter const&
, Attribute const&)
{
return false;
}
};
template <typename Component>
struct alternative_generate<Component, unused_type, unused_type>
{
template <typename OutputIterator, typename Context, typename Delimiter>
static bool
call(Component const& component, OutputIterator& sink, Context& ctx
, Delimiter const& d, unused_type)
{
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
#endif
// return true if any of the generators succeed
return component.generate(sink, ctx, d, unused);
}
};
// this gets instantiated if there is no Attribute given for the
// alternative generator
template <typename Component, typename Expected>
struct alternative_generate<Component, unused_type, Expected>
: alternative_generate<Component, unused_type, unused_type> {};
// this gets instantiated if the generator does not expect to receive an
// Attribute (the generator is self contained).
template <typename Component, typename Attribute>
struct alternative_generate<Component, Attribute, unused_type>
: alternative_generate<Component, unused_type, unused_type> {};
// this gets instantiated if the Attribute type is compatible to the
// generator
template <typename Component, typename Attribute, typename Expected>
struct alternative_generate<Component, Attribute, Expected
, typename enable_if<
compute_compatible_component<Expected, Attribute> >::type>
{
template <typename OutputIterator, typename Context, typename Delimiter>
static bool
call(Component const& component, OutputIterator& sink
, Context& ctx, Delimiter const& d, Attribute const& attr)
{
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
#endif
return call(component, sink, ctx, d, attr
, spirit::traits::not_is_variant<Attribute>());
}
template <typename OutputIterator, typename Context, typename Delimiter>
static bool
call(Component const& component, OutputIterator& sink
, Context& ctx, Delimiter const& d, Attribute const& attr, mpl::true_)
{
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
#endif
return component.generate(sink, ctx, d, attr);
}
template <typename OutputIterator, typename Context, typename Delimiter>
static bool
call(Component const& component, OutputIterator& sink
, Context& ctx, Delimiter const& d, Attribute const& attr, mpl::false_)
{
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
component; // suppresses warning: C4100: 'component' : unreferenced formal parameter
#endif
typedef
compute_compatible_component<Expected, Attribute>
component_type;
typedef typename component_type::distance distance_type;
// if we got passed an empty optional, just fail generation
if (!traits::has_optional_value(attr))
return false;
// make sure, the content of the passed variant matches our
// expectations
typename traits::optional_attribute<Attribute>::type attr_ =
traits::optional_value(attr);
if (attr_.which() != distance_type::value)
return false;
// returns true if any of the generators succeed
typedef
typename mpl::deref<typename component_type::iter>::type
compatible_type;
return component.generate(sink, ctx, d, get<compatible_type>(attr_));
}
};
///////////////////////////////////////////////////////////////////////////
// alternative_generate_function: a functor supplied to fusion::any which
// will be executed for every generator in a given alternative generator
// expression
///////////////////////////////////////////////////////////////////////////
template <typename OutputIterator, typename Context, typename Delimiter,
typename Attribute>
struct alternative_generate_function
{
alternative_generate_function(OutputIterator& sink_, Context& ctx_
, Delimiter const& d, Attribute const& attr_)
: sink(sink_), ctx(ctx_), delim(d), attr(attr_) {}
template <typename Component>
bool operator()(Component const& component)
{
typedef
typename traits::attribute_of<Component, Context>::type
expected_type;
typedef
alternative_generate<Component, Attribute, expected_type>
generate;
// wrap the given output iterator avoid output as long as one
// component fails
detail::enable_buffering<OutputIterator> buffering(sink);
bool r = false;
{
detail::disable_counting<OutputIterator> nocounting(sink);
r = generate::call(component, sink, ctx, delim, attr);
}
if (r)
buffering.buffer_copy();
return r;
}
OutputIterator& sink;
Context& ctx;
Delimiter const& delim;
Attribute const& attr;
private:
// silence MSVC warning C4512: assignment operator could not be generated
alternative_generate_function& operator= (alternative_generate_function const&);
};
}}}}
#endif