Skip to content

Instantly share code, notes, and snippets.

@komori-n
Last active Nov 6, 2020
Embed
What would you like to do?
#include "komori/unique_function.hpp"
#include "gtest/gtest.h"
namespace {
constexpr const int kMagicNumber1 = 0x334;
constexpr const int kMagicNumber2 = 0x668;
int func1(void) {
return kMagicNumber1;
}
struct NonCopyable {
NonCopyable(void) {}
NonCopyable(const NonCopyable&) = delete;
NonCopyable(NonCopyable&&) {}
NonCopyable& operator=(const NonCopyable&) = delete;
int operator()(void) const { return kMagicNumber1; }
};
TEST(unique_function, default_constructor) {
komori::unique_function<int()> f;
EXPECT_FALSE(f);
}
TEST(unique_function, nullptr_constructor) {
komori::unique_function<int()> f(nullptr);
EXPECT_FALSE(f);
}
TEST(unique_function, function_pointer) {
komori::unique_function<int()> f(func1);
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, member_function_pointer) {
}
TEST(unique_function, function_object) {
NonCopyable nc;
komori::unique_function<int()> f(std::move(nc));
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, lambda_constructor) {
komori::unique_function<int()> f{[](void) { return kMagicNumber1; }};
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, move_only_lambda_constructor) {
NonCopyable nc;
komori::unique_function<int()> f{[nc=std::move(nc)](void) { return nc(); }};
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, swap_not_null) {
komori::unique_function<int()> f(func1);
komori::unique_function<int()> g{[](void) { return kMagicNumber2; }};
f.swap(g);
EXPECT_EQ(f(), kMagicNumber2);
EXPECT_EQ(g(), kMagicNumber1);
}
TEST(unique_function, swap_null) {
komori::unique_function<int()> f(func1);
komori::unique_function<int()> g;
f.swap(g);
EXPECT_FALSE(f);
EXPECT_TRUE(g);
EXPECT_EQ(g(), kMagicNumber1);
komori::unique_function<int()> n1;
komori::unique_function<int()> n2;
n1.swap(n2);
EXPECT_FALSE(n1);
EXPECT_FALSE(n2);
}
TEST(unique_function, assign_function_pointer) {
komori::unique_function<int()> f;
f = func1;
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, assign_member_function_pointer) {
}
TEST(unique_function, assign_lambda) {
komori::unique_function<int()> f;
f = [](void) { return kMagicNumber1; };
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, assign_move_only_lambda) {
komori::unique_function<int()> f;
NonCopyable nc;
f = [nc=std::move(nc)](void) { return nc(); };
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
TEST(unique_function, assign_function_object) {
komori::unique_function<int()> f;
NonCopyable nc;
f = std::move(nc);
EXPECT_TRUE(f);
EXPECT_EQ(f(), kMagicNumber1);
}
bool is_deleted = false;
struct IsDeleted {
bool instance;
IsDeleted(void) : instance(true) {}
IsDeleted(const IsDeleted&) = delete;
IsDeleted(IsDeleted&& id) : instance(id.instance) {
id.instance = false;
}
~IsDeleted(void) {
if (instance) {
is_deleted = true;
}
}
int operator()(void) { return static_cast<int>(instance); }
};
TEST(unique_function, assign_nullptr) {
komori::unique_function<int()> f(func1);
EXPECT_TRUE(f);
f = nullptr;
EXPECT_FALSE(f);
IsDeleted im;
EXPECT_FALSE(is_deleted);
komori::unique_function<int()> g(std::move(im));
EXPECT_FALSE(is_deleted);
g = nullptr;
EXPECT_TRUE(is_deleted);
}
}
#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 unique_function;
template <typename Res, typename... ArgTypes>
class unique_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:
unique_function(void) : storage_(nullptr), invoker_(nullptr), deleter_(nullptr) {}
unique_function(nullptr_t) : storage_(nullptr), invoker_(nullptr), deleter_(nullptr) {}
template <typename F, typename DF=std::decay_t<F>>
unique_function(F&& f)
: storage_(new DF(std::forward<F>(f))),
invoker_(&helper<DF>::invoke),
deleter_(&helper<DF>::deleter) {}
unique_function(unique_function&& f) : storage_(f.storage_), invoker_(f.invoker_), deleter_(f.deleter_) {
f.storage_ = nullptr;
f.invoker_ = nullptr;
f.deleter_ = nullptr;
}
template <typename F>
unique_function& operator=(F&& f) {
unique_function(std::forward<F>(f)).swap(*this);
return *this;
}
unique_function(const unique_function&) = delete;
unique_function& operator=(const unique_function&) = delete;
~unique_function(void) {
if (storage_) {
deleter_(storage_);
storage_ = nullptr;
}
}
explicit operator bool(void) const {
return storage_;
}
void swap(unique_function& f) {
std::swap(storage_, f.storage_);
std::swap(invoker_, f.invoker_);
std::swap(deleter_, f.deleter_);
}
Res operator()(ArgTypes&&... args) {
return invoker_(storage_, std::forward<ArgTypes>(args)...);
}
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