275 lines
9.2 KiB
C++
275 lines
9.2 KiB
C++
// Copyright (c) 2006, Giovanni P. Deretta
|
|
//
|
|
// This code may be used under either of the following two licences:
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE. OF SUCH DAMAGE.
|
|
//
|
|
// Or:
|
|
//
|
|
// 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)
|
|
|
|
#ifndef BOOST_COROUTINE_DETAIL_SELF_HPP_20060809
|
|
#define BOOST_COROUTINE_DETAIL_SELF_HPP_20060809
|
|
#include <boost/noncopyable.hpp>
|
|
#include <boost/coroutine/detail/fix_result.hpp>
|
|
#include <boost/coroutine/detail/coroutine_accessor.hpp>
|
|
#include <boost/coroutine/detail/signal.hpp>
|
|
#include <boost/detail/workaround.hpp>
|
|
#include <boost/coroutine/detail/noreturn.hpp>
|
|
|
|
namespace boost { namespace coroutines { namespace detail {
|
|
|
|
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1400))
|
|
#define BOOST_COROUTINE_DEDUCED_TYPENAME_DEFAULT
|
|
#else
|
|
#define BOOST_COROUTINE_DEDUCED_TYPENAME_DEFAULT BOOST_DEDUCED_TYPENAME
|
|
#endif
|
|
|
|
#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1400))
|
|
#define BOOST_COROUTINE_VCPP80_WORKAROUND
|
|
#else
|
|
//for now, define this unconditionally for testing.
|
|
#define BOOST_COROUTINE_VCPP80_WORKAROUND
|
|
#endif
|
|
|
|
template<typename Coroutine>
|
|
class coroutine_self :boost::noncopyable {
|
|
public:
|
|
typedef Coroutine coroutine_type;
|
|
typedef coroutine_self<coroutine_type> type;
|
|
friend struct detail::coroutine_accessor;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::impl_type impl_type;
|
|
|
|
// Note, no reference counting here.
|
|
typedef impl_type * impl_ptr;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::result_type result_type;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::result_slot_type result_slot_type;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::yield_result_type yield_result_type;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::result_slot_traits result_slot_traits;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::arg_slot_type arg_slot_type;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::arg_slot_traits arg_slot_traits;
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME coroutine_type
|
|
::yield_traits yield_traits;
|
|
#ifndef BOOST_COROUTINE_VCPP80_WORKAROUND
|
|
# define BOOST_COROUTINE_param_with_default(z, n, type_prefix) \
|
|
BOOST_DEDUCED_TYPENAME call_traits \
|
|
<BOOST_PP_CAT(BOOST_PP_CAT(type_prefix, n), _type)>::param_type \
|
|
BOOST_PP_CAT(arg, n) = \
|
|
BOOST_PP_CAT(BOOST_PP_CAT(type_prefix, n), _type)() \
|
|
/**/
|
|
|
|
yield_result_type yield
|
|
(BOOST_PP_ENUM
|
|
(BOOST_COROUTINE_ARG_MAX,
|
|
BOOST_COROUTINE_param_with_default,
|
|
BOOST_DEDUCED_TYPENAME yield_traits::arg))
|
|
{
|
|
return yield_impl
|
|
(BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::result_slot_type
|
|
(BOOST_PP_ENUM_PARAMS
|
|
(BOOST_COROUTINE_ARG_MAX,
|
|
arg)));
|
|
}
|
|
|
|
template<typename Target>
|
|
yield_result_type yield_to
|
|
(Target& target
|
|
BOOST_PP_ENUM_TRAILING
|
|
(BOOST_COROUTINE_ARG_MAX,
|
|
BOOST_COROUTINE_param_with_default,
|
|
typename Target::arg))
|
|
{
|
|
typedef BOOST_DEDUCED_TYPENAME Target::arg_slot_type slot_type;
|
|
return yield_to_impl
|
|
(target, slot_type(BOOST_PP_ENUM_PARAMS
|
|
(BOOST_COROUTINE_ARG_MAX,
|
|
arg)));
|
|
}
|
|
#else
|
|
|
|
/*
|
|
* VC8.0 can't handle the call_traits meta-invocation inside
|
|
* a function parameter list (except when it does, see operator()).
|
|
* Splitting it in separate typedefs
|
|
* fixes the problem.
|
|
*/
|
|
#define BOOST_COROUTINE_param_typedef(z, n, prefix_tuple) \
|
|
typedef BOOST_DEDUCED_TYPENAME \
|
|
call_traits< \
|
|
BOOST_PP_CAT \
|
|
(BOOST_PP_CAT \
|
|
(BOOST_PP_TUPLE_ELEM(2, 0, prefix_tuple), n), \
|
|
_type) \
|
|
>::param_type \
|
|
BOOST_PP_CAT \
|
|
(BOOST_PP_CAT \
|
|
(BOOST_PP_TUPLE_ELEM(2, 1, prefix_tuple), n), \
|
|
_type) \
|
|
/**/;
|
|
|
|
/*
|
|
* Generate lines like this:
|
|
* 'typedef typename call_traits<typename coroutine_type::yield_traits::argN_type>::param_type yield_call_argN_type;'
|
|
*/
|
|
BOOST_PP_REPEAT(BOOST_COROUTINE_ARG_MAX,
|
|
BOOST_COROUTINE_param_typedef,
|
|
(BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::yield_traits::arg, yield_call_arg));
|
|
|
|
/*
|
|
* Generate lines like this:
|
|
* 'typedef typename call_traits<typename coroutine_type::argN_type>::param_type call_argN_type;'
|
|
*/
|
|
BOOST_PP_REPEAT(BOOST_COROUTINE_ARG_MAX,
|
|
BOOST_COROUTINE_param_typedef,
|
|
(BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::arg, call_arg));
|
|
|
|
#undef BOOST_COROUTINE_param_typedef
|
|
#undef BOOST_COROUTINE_param_with_default
|
|
#define BOOST_COROUTINE_param_with_default(z, n, prefix_tuple) \
|
|
BOOST_PP_CAT(BOOST_PP_CAT \
|
|
(BOOST_PP_TUPLE_ELEM(2, 0, prefix_tuple), \
|
|
n), _type) \
|
|
BOOST_PP_CAT(arg, n) = \
|
|
BOOST_PP_CAT(BOOST_PP_CAT \
|
|
(BOOST_PP_TUPLE_ELEM(2, 1, prefix_tuple), \
|
|
n), _type)() \
|
|
/**/
|
|
|
|
yield_result_type yield
|
|
(BOOST_PP_ENUM(BOOST_COROUTINE_ARG_MAX,
|
|
BOOST_COROUTINE_param_with_default,
|
|
(yield_call_arg ,
|
|
BOOST_COROUTINE_DEDUCED_TYPENAME_DEFAULT
|
|
coroutine_type::yield_traits::arg)))
|
|
{
|
|
return yield_impl
|
|
(result_slot_type
|
|
(BOOST_PP_ENUM_PARAMS(BOOST_COROUTINE_ARG_MAX,arg)));
|
|
}
|
|
|
|
template<typename Target>
|
|
yield_result_type yield_to
|
|
(Target& target
|
|
BOOST_PP_ENUM_TRAILING
|
|
(BOOST_COROUTINE_ARG_MAX,
|
|
BOOST_COROUTINE_param_with_default,
|
|
(BOOST_DEDUCED_TYPENAME Target::self::call_arg,
|
|
BOOST_COROUTINE_DEDUCED_TYPENAME_DEFAULT Target::arg)))
|
|
{
|
|
typedef typename Target::arg_slot_type type;
|
|
return yield_to_impl
|
|
(target, type
|
|
(BOOST_PP_ENUM_PARAMS
|
|
(BOOST_COROUTINE_ARG_MAX, arg)));
|
|
}
|
|
#endif
|
|
|
|
#undef BOOST_COROUTINE_param_with_default
|
|
|
|
BOOST_COROUTINE_NORETURN(void exit()) {
|
|
m_pimpl -> exit_self();
|
|
abort();
|
|
}
|
|
|
|
yield_result_type result() {
|
|
return detail::fix_result<
|
|
BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::arg_slot_traits>(*m_pimpl->args());
|
|
}
|
|
|
|
bool pending() const {
|
|
BOOST_ASSERT(m_pimpl);
|
|
return m_pimpl->pending();
|
|
}
|
|
|
|
/// @c true only if this @c self object was created by the passed @a coroutine
|
|
template <typename SomeCoroutine>
|
|
bool is_from(const SomeCoroutine& coroutine) const
|
|
{
|
|
// get_impl() only accepts non-const ref... a mistake, IMO.
|
|
return static_cast<void*>(coroutine_accessor::get_impl(const_cast<SomeCoroutine&>(coroutine)).get()) ==
|
|
static_cast<void*>(m_pimpl);
|
|
}
|
|
|
|
/// opaque token used to correlate this 'self' with its corresponding coroutine
|
|
void* get_id() const { return m_pimpl; }
|
|
|
|
private:
|
|
coroutine_self(impl_type * pimpl, detail::init_from_impl_tag) :
|
|
m_pimpl(pimpl) {}
|
|
|
|
yield_result_type yield_impl(BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::result_slot_type result) {
|
|
typedef BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::result_slot_type slot_type;
|
|
|
|
BOOST_ASSERT(m_pimpl);
|
|
|
|
this->m_pimpl->bind_result(&result);
|
|
this->m_pimpl->yield();
|
|
return detail::fix_result<
|
|
BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::arg_slot_traits>(*m_pimpl->args());
|
|
}
|
|
|
|
template<typename TargetCoroutine>
|
|
yield_result_type yield_to_impl(TargetCoroutine& target,
|
|
BOOST_DEDUCED_TYPENAME TargetCoroutine
|
|
::arg_slot_type args) {
|
|
BOOST_ASSERT(m_pimpl);
|
|
|
|
coroutine_accessor::get_impl(target)->bind_args(&args);
|
|
coroutine_accessor::get_impl(target)->bind_result_pointer(m_pimpl->result_pointer());
|
|
|
|
this->m_pimpl->yield_to(*coroutine_accessor::get_impl(target));
|
|
|
|
return detail::fix_result<
|
|
BOOST_DEDUCED_TYPENAME
|
|
coroutine_type::arg_slot_traits>(*m_pimpl->args());
|
|
}
|
|
|
|
impl_ptr get_impl() {
|
|
return m_pimpl;
|
|
}
|
|
impl_ptr m_pimpl;
|
|
};
|
|
} } }
|
|
|
|
#endif
|