Created
April 27, 2012 08:12
-
-
Save yohhoy/2507350 to your computer and use it in GitHub Desktop.
once function(+binder)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <tuple> | |
#include <memory> | |
#include <functional> // bad_function_call | |
#include <utility> | |
#include <type_traits> | |
namespace detail { | |
// index_tuple<size_t...>, make_indices<E,S> | |
template <size_t...> struct index_tuple {}; | |
template <size_t S, class Tuple, size_t E> | |
struct make_indices_impl; | |
template <size_t S, size_t... Indices, size_t E> | |
struct make_indices_impl<S, index_tuple<Indices...>, E> | |
{ | |
typedef typename make_indices_impl<S+1, index_tuple<Indices..., S>, E>::type type; | |
}; | |
template <size_t E, size_t... Indices> | |
struct make_indices_impl<E, index_tuple<Indices...>, E> | |
{ | |
typedef index_tuple<Indices...> type; | |
}; | |
template <size_t E, size_t S = 0> | |
struct make_indices | |
{ | |
typedef typename make_indices_impl<S, index_tuple<>, E>::type type; | |
}; | |
// INVOKE(f, args...) | |
#if 0 //TODO: use SFIANE for function dispatch | |
template <class F, class A0, class... Args> | |
auto invoke(F&& f, A0&& a0, Args&&... args) | |
-> decltype((std::forward<A0>(a0).*f)(std::forward<Args>(args)...)) | |
{ | |
return (std::forward<A0>(a0).*f)(std::forward<Args>(args)...); | |
} | |
template <class F, class A0, class... Args> | |
auto invoke(F&& f, A0&& a0, Args&&... args) | |
-> decltype(((*std::forward<A0>(a0)).*f)(std::forward<Args>(args)...)) | |
{ | |
return ((*std::forward<A0>(a0)).*f)(std::forward<Args>(args)...); | |
} | |
template <class F, class A0> | |
auto invoke(F&& f, A0&& a0) | |
-> decltype(std::forward<A0>(a0).*f) | |
{ | |
return std::forward<A0>(a0).*f; | |
} | |
template <class F, class A0> | |
auto invoke(F&& f, A0&& a0) | |
-> decltype((*std::forward<A0>(a0)).*f) | |
{ | |
return (*std::forward<A0>(a0)).*f; | |
} | |
#endif | |
template <class F, class... Args> | |
auto invoke(F&& f, Args&&... args) | |
-> decltype(std::forward<F>(f)(std::forward<Args>(args)...)) | |
{ | |
return std::forward<F>(f)(std::forward<Args>(args)...); | |
} | |
// DECAY_COPY(v) | |
template <class T> | |
typename std::decay<T>::type decay_copy(T&& v) | |
{ | |
return std::forward<T>(v); | |
} | |
// once_function_base<R> | |
template <class R> | |
class once_function_base { | |
public: | |
typedef R result_type; | |
once_function_base() = default; | |
template <class F, class... Args> | |
explicit once_function_base(F&& f, Args&&... args) | |
: impl_(new invoker_impl<F, Args...>(std::forward<F>(f), std::forward<Args>(args)...)) | |
{} | |
once_function_base(const once_function_base&) = delete; | |
once_function_base& operator=(const once_function_base&) = delete; | |
once_function_base(once_function_base&&) = default; | |
once_function_base& operator=(once_function_base&&) = default; | |
protected: | |
struct invoker_base { | |
virtual result_type invoke() = 0; | |
}; | |
template <class F, class... Args> | |
struct invoker_impl : invoker_base { | |
typedef std::tuple<typename std::decay<F>::type, typename std::decay<Args>::type...> PackTuple; | |
PackTuple pack_; | |
invoker_impl(F&& f, Args&&... args) | |
: pack_(decay_copy(std::forward<F>(f)), decay_copy(std::forward<Args>(args))...) {} | |
template <size_t... Indices> | |
result_type do_invoke(detail::index_tuple<Indices...>) | |
{ | |
return detail::invoke(std::move(std::get<0>(pack_)), std::move(std::get<Indices>(pack_))...); | |
} | |
virtual result_type invoke() /*override*/ | |
{ | |
return do_invoke(typename make_indices<std::tuple_size<PackTuple>::value, 1>::type()); | |
} | |
}; | |
std::unique_ptr<invoker_base> impl_; | |
}; | |
} // namespace detail | |
template <class R> | |
class once_function : private detail::once_function_base<R> { | |
using detail::once_function_base<R>::impl_; | |
public: | |
typedef R result_type; | |
once_function() = default; | |
template <class F, class... Args> | |
explicit once_function(F&& f, Args&&... args) | |
: detail::once_function_base<R>(std::forward<F>(f), std::forward<Args>(args)...) {} | |
result_type operator()() | |
{ | |
if (!impl_) | |
throw std::bad_function_call(); | |
result_type result = impl_->invoke(); | |
impl_.reset(); | |
return result; | |
} | |
}; | |
template <> | |
class once_function<void> : private detail::once_function_base<void> { | |
using detail::once_function_base<void>::impl_; | |
public: | |
typedef void result_type; | |
once_function() = default; | |
template <class F, class... Args> | |
explicit once_function(F&& f, Args&&... args) | |
: detail::once_function_base<void>(std::forward<F>(f), std::forward<Args>(args)...) {} | |
result_type operator()() | |
{ | |
if (!impl_) | |
throw std::bad_function_call(); | |
impl_->invoke(); | |
impl_.reset(); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment