2
0
mirror of https://github.com/boostorg/asio.git synced 2026-01-27 18:42:07 +00:00

Merge from trunk.

........
  r49155 | nmusatti | 2008-10-07 08:46:14 +1100 (Tue, 07 Oct 2008) | 1 line
  
  Patch from  Ticket #2372
........
  r49195 | chris_kohlhoff | 2008-10-09 17:22:58 +1100 (Thu, 09 Oct 2008) | 2 lines
  
  Add missing bounds checks as specified in TR2 proposal.
........
  r49197 | chris_kohlhoff | 2008-10-09 17:28:39 +1100 (Thu, 09 Oct 2008) | 2 lines
  
  Merge codegear changes from non-boost version of asio.
........
  r49198 | chris_kohlhoff | 2008-10-09 17:30:16 +1100 (Thu, 09 Oct 2008) | 4 lines
  
  Ensure the streambuf's egptr() is kept in sync the pptr(). Use std::memmove
  rather than std::rotate to minimise data copying. Avoid unnecessary resizes
  of the underlying vector.
........
  r49199 | chris_kohlhoff | 2008-10-09 17:31:01 +1100 (Thu, 09 Oct 2008) | 3 lines
  
  Fix basic_socket_streambuf to work with Protocol objects that don't
  provide a resolver.
........
  r49200 | chris_kohlhoff | 2008-10-09 17:32:00 +1100 (Thu, 09 Oct 2008) | 2 lines
  
  Add example showing use of local::stream_protocol::iostream.
........
  r49201 | chris_kohlhoff | 2008-10-09 17:33:34 +1100 (Thu, 09 Oct 2008) | 4 lines
  
  Only use TerminateThread when explicitly requested by the user by calling
  asio::detail::thread::set_terminate_threads(true). This fixes a memory leak
  that may occur with internally created threads.
........
  r49202 | chris_kohlhoff | 2008-10-09 17:34:48 +1100 (Thu, 09 Oct 2008) | 3 lines
  
  Make the service_registry's usage of typeid work when the default gcc
  linker visibility is set to hidden.
........
  r49203 | chris_kohlhoff | 2008-10-09 17:39:05 +1100 (Thu, 09 Oct 2008) | 2 lines
  
  Reduce memory usage by doing lazy initialisation of the io_service's reactor.
........


[SVN r49221]
This commit is contained in:
Christopher Kohlhoff
2008-10-09 20:43:26 +00:00
parent 8e413b0ece
commit 2fac9863bc
23 changed files with 333 additions and 84 deletions

View File

@@ -33,6 +33,18 @@ exe connect_pair
$(SOCKET_LIBS)
;
exe iostream_client
: <lib>@boost/libs/system/build/boost_system
iostream_client.cpp
: <include>$(BOOST_ROOT)
<include>../../../..
<define>BOOST_ALL_NO_LIB=1
<threading>multi
<mingw><*><find-library>ws2_32
<mingw><*><find-library>mswsock
$(SOCKET_LIBS)
;
exe stream_client
: <lib>@boost/libs/system/build/boost_system
stream_client.cpp

View File

@@ -38,6 +38,21 @@ exe connect_pair
<os>HPUX:<library>ipv6
;
exe iostream_client
: iostream_client.cpp
/boost/system//boost_system
: <define>BOOST_ALL_NO_LIB=1
<threading>multi
<os>SOLARIS:<library>socket
<os>SOLARIS:<library>nsl
<os>NT:<define>_WIN32_WINNT=0x0501
<os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>mswsock
<os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
<os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
<os>HPUX:<library>ipv6
;
exe stream_client
: stream_client.cpp
/boost/system//boost_system

View File

@@ -0,0 +1,59 @@
//
// stream_client.cpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// 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)
//
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
using boost::asio::local::stream_protocol;
enum { max_length = 1024 };
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: iostream_client <file>\n";
return 1;
}
boost::asio::io_service io_service;
stream_protocol::endpoint ep(argv[1]);
stream_protocol::iostream s(ep);
using namespace std; // For strlen.
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t length = strlen(request);
s << request;
char reply[max_length];
s.read(reply, length);
std::cout << "Reply is: ";
std::cout.write(reply, length);
std::cout << "\n";
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
#else // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
# error Local sockets not available on this platform.
#endif // defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)

View File

@@ -248,7 +248,8 @@ private:
setp(put_buffer_.begin(), put_buffer_.end());
}
void resolve_and_connect(const typename Protocol::resolver_query& query,
template <typename ResolverQuery>
void resolve_and_connect(const ResolverQuery& query,
boost::system::error_code& ec)
{
typedef typename Protocol::resolver resolver_type;

View File

@@ -19,6 +19,7 @@
#include <boost/asio/detail/push_options.hpp>
#include <algorithm>
#include <cstring>
#include <limits>
#include <memory>
#include <stdexcept>
@@ -96,6 +97,7 @@ public:
if (pptr() + n > epptr())
n = epptr() - pptr();
pbump(static_cast<int>(n));
setg(eback(), gptr(), pptr());
}
/// Move the start of the get area by the specified number of characters.
@@ -151,7 +153,6 @@ protected:
{
// Get current stream positions as offsets.
std::size_t gnext = gptr() - &buffer_[0];
std::size_t gend = egptr() - &buffer_[0];
std::size_t pnext = pptr() - &buffer_[0];
std::size_t pend = epptr() - &buffer_[0];
@@ -164,9 +165,8 @@ protected:
// Shift existing contents of get area to start of buffer.
if (gnext > 0)
{
std::rotate(&buffer_[0], &buffer_[0] + gnext, &buffer_[0] + pend);
gend -= gnext;
pnext -= gnext;
std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext);
}
// Ensure buffer is large enough to hold at least the specified size.
@@ -174,7 +174,8 @@ protected:
{
if (n <= max_size_ && pnext <= max_size_ - n)
{
buffer_.resize((std::max<std::size_t>)(pnext + n, 1));
pend = pnext + n;
buffer_.resize((std::max<std::size_t>)(pend, 1));
}
else
{
@@ -183,8 +184,8 @@ protected:
}
// Update stream positions.
setg(&buffer_[0], &buffer_[0], &buffer_[0] + gend);
setp(&buffer_[0] + pnext, &buffer_[0] + pnext + n);
setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext);
setp(&buffer_[0] + pnext, &buffer_[0] + pend);
}
private:

View File

@@ -64,6 +64,7 @@ public:
deadline_timer_service<Time_Traits, Timer_Scheduler> >(io_service),
scheduler_(boost::asio::use_service<Timer_Scheduler>(io_service))
{
scheduler_.init_task();
scheduler_.add_timer_queue(timer_queue_);
}

View File

@@ -123,6 +123,17 @@ public:
timer_queues_.clear();
}
// Initialise the task, but only if the reactor is not in its own thread.
void init_task()
{
if (!Own_Thread)
{
typedef task_io_service<dev_poll_reactor<Own_Thread> >
task_io_service_type;
use_service<task_io_service_type>(this->get_io_service()).init_task();
}
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type, per_descriptor_data&)

View File

@@ -124,6 +124,16 @@ public:
timer_queues_.clear();
}
// Initialise the task, but only if the reactor is not in its own thread.
void init_task()
{
if (!Own_Thread)
{
typedef task_io_service<epoll_reactor<Own_Thread> > task_io_service_type;
use_service<task_io_service_type>(this->get_io_service()).init_task();
}
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type descriptor,

View File

@@ -132,6 +132,16 @@ public:
timer_queues_.clear();
}
// Initialise the task, but only if the reactor is not in its own thread.
void init_task()
{
if (!Own_Thread)
{
typedef task_io_service<kqueue_reactor<Own_Thread> > task_io_service_type;
use_service<task_io_service_type>(this->get_io_service()).init_task();
}
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type, per_descriptor_data& descriptor_data)

View File

@@ -39,12 +39,9 @@ class null_thread
: private noncopyable
{
public:
// The purpose of the thread.
enum purpose { internal, external };
// Constructor.
template <typename Function>
null_thread(Function f, purpose = internal)
null_thread(Function f)
{
boost::system::system_error e(
boost::asio::error::operation_not_supported, "thread");

View File

@@ -43,12 +43,9 @@ class posix_thread
: private noncopyable
{
public:
// The purpose of the thread.
enum purpose { internal, external };
// Constructor.
template <typename Function>
posix_thread(Function f, purpose = internal)
posix_thread(Function f)
: joined_(false)
{
std::auto_ptr<func_base> arg(new func<Function>(f));

View File

@@ -82,6 +82,7 @@ public:
reactive_descriptor_service<Reactor> >(io_service),
reactor_(boost::asio::use_service<Reactor>(io_service))
{
reactor_.init_task();
}
// Destroy all user-defined handler objects owned by the service.

View File

@@ -110,6 +110,7 @@ public:
reactive_socket_service<Protocol, Reactor> >(io_service),
reactor_(boost::asio::use_service<Reactor>(io_service))
{
reactor_.init_task();
}
// Destroy all user-defined handler objects owned by the service.

View File

@@ -111,6 +111,16 @@ public:
timer_queues_.clear();
}
// Initialise the task, but only if the reactor is not in its own thread.
void init_task()
{
if (!Own_Thread)
{
typedef task_io_service<select_reactor<Own_Thread> > task_io_service_type;
use_service<task_io_service_type>(this->get_io_service()).init_task();
}
}
// Register a socket with the reactor. Returns 0 on success, system error
// code on failure.
int register_descriptor(socket_type, per_descriptor_data&)

View File

@@ -37,6 +37,21 @@ namespace boost {
namespace asio {
namespace detail {
#if defined(__GNUC__)
# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
# pragma GCC visibility push (default)
# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
#endif // defined(__GNUC__)
template <typename T>
class typeid_wrapper {};
#if defined(__GNUC__)
# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
# pragma GCC visibility pop
# endif // (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
#endif // defined(__GNUC__)
class service_registry
: private noncopyable
{
@@ -169,7 +184,7 @@ private:
void init_service_id(boost::asio::io_service::service& service,
const boost::asio::detail::service_id<Service>& /*id*/)
{
service.type_info_ = &typeid(Service);
service.type_info_ = &typeid(typeid_wrapper<Service>);
service.id_ = 0;
}
#endif // !defined(BOOST_ASIO_NO_TYPEID)
@@ -189,7 +204,8 @@ private:
const boost::asio::io_service::service& service,
const boost::asio::detail::service_id<Service>& /*id*/)
{
return service.type_info_ != 0 && *service.type_info_ == typeid(Service);
return service.type_info_ != 0
&& *service.type_info_ == typeid(typeid_wrapper<Service>);
}
#endif // !defined(BOOST_ASIO_NO_TYPEID)

View File

@@ -44,14 +44,13 @@ public:
task_io_service(boost::asio::io_service& io_service)
: boost::asio::detail::service_base<task_io_service<Task> >(io_service),
mutex_(),
task_(use_service<Task>(io_service)),
task_(0),
task_interrupted_(true),
outstanding_work_(0),
stopped_(false),
shutdown_(false),
first_idle_thread_(0)
{
handler_queue_.push(&task_handler_);
}
void init(size_t /*concurrency_hint*/)
@@ -74,8 +73,20 @@ public:
h->destroy();
}
// Reset handler queue to initial state.
handler_queue_.push(&task_handler_);
// Reset to initial state.
task_ = 0;
}
// Initialise the task, if required.
void init_task()
{
boost::asio::detail::mutex::scoped_lock lock(mutex_);
if (!shutdown_ && !task_)
{
task_ = &use_service<Task>(this->get_io_service());
handler_queue_.push(&task_handler_);
interrupt_one_idle_thread(lock);
}
}
// Run the event loop until interrupted or no more work.
@@ -194,10 +205,10 @@ public:
// Wake up a thread to execute the handler.
if (!interrupt_one_idle_thread(lock))
{
if (!task_interrupted_)
if (!task_interrupted_ && task_)
{
task_interrupted_ = true;
task_.interrupt();
task_->interrupt();
}
}
}
@@ -246,7 +257,7 @@ private:
// Run the task. May throw an exception. Only block if the handler
// queue is empty and we have an idle_thread_info object, otherwise
// we want to return as soon as possible.
task_.run(!more_handlers && !polling);
task_->run(!more_handlers && !polling);
}
else
{
@@ -285,10 +296,10 @@ private:
{
stopped_ = true;
interrupt_all_idle_threads(lock);
if (!task_interrupted_)
if (!task_interrupted_ && task_)
{
task_interrupted_ = true;
task_.interrupt();
task_->interrupt();
}
}
@@ -376,7 +387,7 @@ private:
boost::asio::detail::mutex mutex_;
// The task to be run by this service.
Task& task_;
Task* task_;
// Handler object to represent the position of the task in the queue.
class task_handler

View File

@@ -50,7 +50,7 @@ public:
: boost::asio::detail::service_base<task_io_service<Task> >(io_service),
front_mutex_(),
back_mutex_(),
task_(use_service<Task>(io_service)),
task_(&use_service<Task>(io_service)),
outstanding_work_(0),
front_stopped_(false),
back_stopped_(false),
@@ -58,7 +58,6 @@ public:
back_first_idle_thread_(0),
back_task_thread_(0)
{
handler_queue_.push(&task_handler_);
}
void init(size_t /*concurrency_hint*/)
@@ -77,8 +76,20 @@ public:
if (h != &task_handler_)
h->destroy();
// Reset handler queue to initial state.
handler_queue_.push(&task_handler_);
// Reset to initial state.
task_ = 0;
}
// Initialise the task, if required.
void init_task()
{
boost::asio::detail::mutex::scoped_lock back_lock(back_mutex_);
if (!back_shutdown_ && !task_)
{
task_ = &use_service<Task>(this->get_io_service());
handler_queue_.push(&task_handler_);
interrupt_one_idle_thread(back_lock);
}
}
// Run the event loop until interrupted or no more work.
@@ -287,7 +298,7 @@ private:
// queue is empty and we're not polling, otherwise we want to return
// as soon as possible.
task_has_run = true;
task_.run(!more_handlers && !polling);
task_->run(!more_handlers && !polling);
}
else
{
@@ -342,10 +353,10 @@ private:
idle_thread->next = 0;
idle_thread->wakeup_event.signal(back_lock);
}
else if (back_task_thread_)
else if (back_task_thread_ && task_)
{
back_task_thread_ = 0;
task_.interrupt();
task_->interrupt();
}
}
@@ -361,10 +372,10 @@ private:
idle_thread->wakeup_event.signal(back_lock);
}
if (back_task_thread_)
if (back_task_thread_ && task_)
{
back_task_thread_ = 0;
task_.interrupt();
task_->interrupt();
}
}
@@ -415,7 +426,7 @@ private:
boost::asio::detail::mutex back_mutex_;
// The task to be run by this service.
Task& task_;
Task* task_;
// Handler object to represent the position of the task in the queue.
class task_handler

View File

@@ -149,6 +149,11 @@ public:
timer_queues_.clear();
}
// Initialise the task. Nothing to do here.
void init_task()
{
}
// Register a handle with the IO completion port.
boost::system::error_code register_handle(
HANDLE handle, boost::system::error_code& ec)

View File

@@ -40,50 +40,67 @@ namespace detail {
unsigned int __stdcall win_thread_function(void* arg);
class win_thread
: private noncopyable
#if (WINVER < 0x0500)
void __stdcall apc_function(ULONG data);
#else
void __stdcall apc_function(ULONG_PTR data);
#endif
template <typename T>
class win_thread_base
{
public:
// The purpose of the thread.
enum purpose { internal, external };
static bool terminate_threads()
{
return ::InterlockedExchangeAdd(&terminate_threads_, 0) != 0;
}
static void set_terminate_threads(bool b)
{
::InterlockedExchange(&terminate_threads_, b ? 1 : 0);
}
private:
static long terminate_threads_;
};
template <typename T>
long win_thread_base<T>::terminate_threads_ = 0;
class win_thread
: private noncopyable,
public win_thread_base<win_thread>
{
public:
// Constructor.
template <typename Function>
win_thread(Function f, purpose p = internal)
win_thread(Function f)
: exit_event_(0)
{
std::auto_ptr<func_base> arg(new func<Function>(f));
::HANDLE entry_event = 0;
if (p == internal)
arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0);
if (!entry_event)
{
arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0);
if (!entry_event)
{
DWORD last_error = ::GetLastError();
boost::system::system_error e(
boost::system::error_code(last_error,
boost::asio::error::get_system_category()),
"thread.entry_event");
boost::throw_exception(e);
}
arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0);
if (!exit_event_)
{
DWORD last_error = ::GetLastError();
::CloseHandle(entry_event);
boost::system::system_error e(
boost::system::error_code(last_error,
boost::asio::error::get_system_category()),
"thread.exit_event");
boost::throw_exception(e);
}
DWORD last_error = ::GetLastError();
boost::system::system_error e(
boost::system::error_code(last_error,
boost::asio::error::get_system_category()),
"thread.entry_event");
boost::throw_exception(e);
}
else
arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0);
if (!exit_event_)
{
arg->entry_event_ = 0;
arg->exit_event_ = 0;
DWORD last_error = ::GetLastError();
::CloseHandle(entry_event);
boost::system::system_error e(
boost::system::error_code(last_error,
boost::asio::error::get_system_category()),
"thread.exit_event");
boost::throw_exception(e);
}
unsigned int thread_id = 0;
@@ -123,14 +140,15 @@ public:
// Wait for the thread to exit.
void join()
{
if (exit_event_)
::WaitForSingleObject(exit_event_, INFINITE);
::CloseHandle(exit_event_);
if (terminate_threads())
{
::WaitForSingleObject(exit_event_, INFINITE);
::CloseHandle(exit_event_);
::TerminateThread(thread_, 0);
}
else
{
::QueueUserAPC(apc_function, thread_, 0);
::WaitForSingleObject(thread_, INFINITE);
}
}
@@ -138,6 +156,12 @@ public:
private:
friend unsigned int __stdcall win_thread_function(void* arg);
#if (WINVER < 0x0500)
friend void __stdcall apc_function(ULONG);
#else
friend void __stdcall apc_function(ULONG_PTR);
#endif
class func_base
{
public:
@@ -175,21 +199,30 @@ inline unsigned int __stdcall win_thread_function(void* arg)
std::auto_ptr<win_thread::func_base> func(
static_cast<win_thread::func_base*>(arg));
if (func->entry_event_)
::SetEvent(func->entry_event_);
::SetEvent(func->entry_event_);
func->run();
if (HANDLE exit_event = func->exit_event_)
{
func.reset();
::SetEvent(exit_event);
::Sleep(INFINITE);
}
// Signal that the thread has finished its work, but rather than returning go
// to sleep to put the thread into a well known state. If the thread is being
// joined during global object destruction then it may be killed using
// TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx
// call will be interrupted using QueueUserAPC and the thread will shut down
// cleanly.
HANDLE exit_event = func->exit_event_;
func.reset();
::SetEvent(exit_event);
::SleepEx(INFINITE, TRUE);
return 0;
}
#if (WINVER < 0x0500)
inline void __stdcall apc_function(ULONG) {}
#else
inline void __stdcall apc_function(ULONG_PTR) {}
#endif
} // namespace detail
} // namespace asio
} // namespace boost

View File

@@ -43,12 +43,9 @@ class wince_thread
: private noncopyable
{
public:
// The purpose of the thread.
enum purpose { internal, external };
// Constructor.
template <typename Function>
wince_thread(Function f, purpose = internal)
wince_thread(Function f)
{
std::auto_ptr<func_base> arg(new func<Function>(f));
DWORD thread_id = 0;

View File

@@ -18,7 +18,9 @@
#include <boost/asio/detail/push_options.hpp>
#include <boost/asio/detail/push_options.hpp>
#include <climits>
#include <string>
#include <stdexcept>
#include <boost/array.hpp>
#include <boost/throw_exception.hpp>
#include <boost/asio/detail/pop_options.hpp>
@@ -56,6 +58,15 @@ public:
/// Construct an address from raw bytes.
explicit address_v4(const bytes_type& bytes)
{
#if UCHAR_MAX > 0xFF
if (bytes[0] > 0xFF || bytes[1] > 0xFF
|| bytes[2] > 0xFF || bytes[3] > 0xFF)
{
std::out_of_range ex("address_v4 from bytes_type");
boost::throw_exception(ex);
}
#endif // UCHAR_MAX > 0xFF
using namespace std; // For memcpy.
memcpy(&addr_.s_addr, bytes.elems, 4);
}
@@ -63,6 +74,14 @@ public:
/// Construct an address from a unsigned long in host byte order.
explicit address_v4(unsigned long addr)
{
#if ULONG_MAX > 0xFFFFFFFF
if (addr > 0xFFFFFFFF)
{
std::out_of_range ex("address_v4 from unsigned long");
boost::throw_exception(ex);
}
#endif // ULONG_MAX > 0xFFFFFFFF
addr_.s_addr = boost::asio::detail::socket_ops::host_to_network_long(addr);
}

View File

@@ -63,6 +63,17 @@ public:
explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0)
: scope_id_(scope_id)
{
#if UCHAR_MAX > 0xFF
for (std::size_t i = 0; i < bytes.size(); ++i)
{
if (bytes[i] > 0xFF)
{
std::out_of_range ex("address_v6 from bytes_type");
boost::throw_exception(ex);
}
}
#endif // UCHAR_MAX > 0xFF
using namespace std; // For memcpy.
memcpy(addr_.s6_addr, bytes.elems, 16);
}
@@ -166,7 +177,11 @@ public:
address_v4 to_v4() const
{
if (!is_v4_mapped() && !is_v4_compatible())
throw std::bad_cast();
{
std::bad_cast ex;
boost::throw_exception(ex);
}
address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12],
addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } };
return address_v4(v4_bytes);

View File

@@ -24,6 +24,7 @@
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/detail/workaround.hpp>
#include <string>
#include <boost/asio/detail/pop_options.hpp>
@@ -35,6 +36,20 @@ namespace asio {
namespace detail
{
#if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x610))
template <typename T>
struct has_result_type
{
template <typename U> struct inner
{
struct big { char a[100]; };
static big helper(U, ...);
static char helper(U, typename U::result_type* = 0);
};
static const T& ref();
enum { value = (sizeof((inner<const T&>::helper)((ref)())) == 1) };
};
#else // BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x610))
template <typename T>
struct has_result_type
{
@@ -44,6 +59,7 @@ namespace detail
static const T& ref();
enum { value = (sizeof((helper)((ref)())) == 1) };
};
#endif // BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x610))
} // namespace detail
/// Type trait used to determine whether a type can be used as a match condition