Skip to content

Instantly share code, notes, and snippets.

@yohhoy
Created April 27, 2012 08:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yohhoy/2507350 to your computer and use it in GitHub Desktop.
Save yohhoy/2507350 to your computer and use it in GitHub Desktop.
once function(+binder)
#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