Skip to content

Instantly share code, notes, and snippets.

@komori-n
Last active November 6, 2020 11:45
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 komori-n/5a5240441d95764ea12928f7a3a171e7 to your computer and use it in GitHub Desktop.
Save komori-n/5a5240441d95764ea12928f7a3a171e7 to your computer and use it in GitHub Desktop.
#pragma once
#include <utility>
#include <functional>
namespace komori {
namespace detail {
template <typename F, typename Res, typename... ArgTypes>
struct invoke_helper {
static Res invoke(void* storage, ArgTypes&&... args) {
return
std::invoke(*static_cast<F*>(storage), std::forward<ArgTypes>(args)...);
}
static void deleter(void* storage) {
delete static_cast<F*>(storage);
}
};
}
template <typename T>
class onetime_function;
template <typename Res, typename... ArgTypes>
class onetime_function<Res(ArgTypes...)> {
using invoker_t = Res(*)(void*, ArgTypes&&...);
using deleter_t = void(*)(void*);
template <typename F>
using helper = detail::invoke_helper<F, Res, ArgTypes...>;
public:
onetime_function(void) : storage_(nullptr), invoker_(nullptr), deleter_(nullptr) {}
onetime_function(nullptr_t) : storage_(nullptr), invoker_(nullptr), deleter_(nullptr) {}
template <typename F, typename DF=std::decay_t<F>>
onetime_function(F&& f)
: storage_(new DF(std::forward<F>(f))),
invoker_(&helper<DF>::invoke),
deleter_(&helper<DF>::deleter) {}
onetime_function(onetime_function&& f) : storage_(f.storage_), invoker_(f.invoker_), deleter_(f.deleter_) {
f.storage_ = nullptr;
f.invoker_ = nullptr;
f.deleter_ = nullptr;
}
template <typename F>
onetime_function& operator=(F&& f) {
onetime_function(std::forward<F>(f)).swap(*this);
return *this;
}
onetime_function(const onetime_function&) = delete;
onetime_function& operator=(const onetime_function&) = delete;
~onetime_function(void) {
if (storage_) {
deleter_(storage_);
storage_ = nullptr;
invoker_ = nullptr;
deleter_ = nullptr;
}
}
explicit operator bool(void) const {
return storage_;
}
void swap(onetime_function& f) {
std::swap(storage_, f.storage_);
std::swap(invoker_, f.invoker_);
std::swap(deleter_, f.deleter_);
}
template <std::nullptr_t Dummy = nullptr>
auto operator()(ArgTypes&&... args)
-> std::enable_if_t<!std::is_same<Res, void>::value && Dummy == nullptr, Res> {
if (storage_) {
auto&& res = invoker_(storage_, std::forward<ArgTypes>(args)...);
onetime_function().swap(*this);
return std::forward<Res>(res);
} else {
throw std::runtime_error("storage is null");
}
}
template <std::nullptr_t Dummy = nullptr>
auto operator()(ArgTypes&&... args)
-> std::enable_if_t<std::is_same<Res, void>::value && Dummy == nullptr, Res> {
if (storage_) {
invoker_(storage_, std::forward<ArgTypes>(args)...);
onetime_function().swap(*this);
} else {
throw std::runtime_error("storage is null");
}
}
private:
void* storage_;
invoker_t invoker_;
deleter_t deleter_;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment