Skip to content

Instantly share code, notes, and snippets.

@ecatmur
Last active May 24, 2016 07:33
Show Gist options
  • Save ecatmur/e9230236c6f7636c417f0c2b2d77037f to your computer and use it in GitHub Desktop.
Save ecatmur/e9230236c6f7636c417f0c2b2d77037f to your computer and use it in GitHub Desktop.
#include <functional>
#include <iostream>
#include <new>
#include <utility>
struct SmallBufferTable
{
void (*destructor)(void const*);
void (*copier)(void*, void const*);
void (*mover)(void*, void*);
template<class T>
static void destruct(void const* t) { static_cast<T const*>(t)->~T(); }
template<class T>
static void copy(void* t, void const* o) { new (t) T(*static_cast<T const*>(o)); }
template<class T>
static void move(void* t, void* o) { new (t) T(std::move(*static_cast<T*>(o))); }
template<class T>
struct For { static SmallBufferTable const value; };
};
template<class T>
SmallBufferTable const SmallBufferTable::For<T>::value = {
&SmallBufferTable::destruct<T>,
&SmallBufferTable::copy<T>,
&SmallBufferTable::move<T>,
};
template<std::size_t Size>
class SmallBuffer
{
protected:
std::aligned_storage_t<Size, std::alignment_of<void*>::value> buf;
SmallBufferTable const* table;
SmallBuffer() {
table = nullptr;
}
template<class T>
SmallBuffer(T&& t) { assign(std::forward<T>(t)); }
template<class T>
void assign(T&& t) {
using TT = std::decay_t<T>;
static_assert(sizeof(TT) <= sizeof(buf));
static_assert(std::alignment_of<TT>::value <= std::alignment_of<decltype(buf)>::value);
new (&buf) TT(std::forward<T>(t));
table = &SmallBufferTable::template For<TT>::value;
}
SmallBuffer(SmallBuffer const& other) {
if ((table = other.table))
table->copier(&buf, &other.buf);
}
SmallBuffer(SmallBuffer&& other) {
if ((table = other.table)) {
table->mover(&buf, &other.buf);
other.table->destructor(&other.buf);
other.table = nullptr;
}
}
SmallBuffer& operator=(SmallBuffer const& other) {
if (table)
table->destructor(&buf);
if (other.table)
other.table->copier(&buf, &other.buf);
table = other.table;
return *this;
}
SmallBuffer& operator=(SmallBuffer&& other) {
if (table) {
table->destructor(&buf);
table = nullptr;
}
if (other.table) {
other.table->mover(&buf, &other.buf);
table = other.controller;
other.table->destructor(&other.buf);
other.table = nullptr;
}
return *this;
}
~SmallBuffer() {
if (table)
table->destructor(&buf);
}
bool operator!() const { return !table; }
explicit operator bool() const { return !!*this; }
};
using SmallFunctionBuffer = SmallBuffer<sizeof(void*) * 2>;
template<class Signature, class Derived>
class FunctionInvoker;
template<class Ret, class... Args, class Derived>
class FunctionInvoker<Ret(Args...), Derived>
{
protected:
using Invoker = Ret(void const*, Args...);
Invoker* invoker;
FunctionInvoker(Invoker* i) : invoker{i} {};
template<class F>
static Ret invoke(void const* obj, Args... args) {
return (*reinterpret_cast<std::decay_t<F> const*>(obj))
(std::forward<Args>(args)...);
}
static Ret invokeBad(void const*, Args...) {
throw std::bad_function_call();
}
Ret operator()(Args... args) const {
return invoker(Derived::get(this), std::forward<Args>(args)...);
}
};
template<class Signature>
class SmallFunction
: private SmallFunctionBuffer
, private FunctionInvoker<Signature, SmallFunction<Signature>>
{
using This = SmallFunction<Signature>;
using Invoker = FunctionInvoker<Signature, This>;
friend Invoker;
static void const* get(Invoker const* self) {
return &static_cast<SmallFunction const*>(self)->buf;
}
public:
SmallFunction()
: SmallFunctionBuffer{}
, Invoker{&Invoker::invokeBad} {}
template<class F>
SmallFunction(F&& f)
: SmallFunctionBuffer(std::forward<F>(f))
, Invoker{&Invoker::template invoke<F>} {}
using Invoker::operator();
using SmallFunctionBuffer::operator bool;
};
double f(SmallFunction<double(int, int)>& x, int i, int j) {
return x(i, j);
}
bool g(SmallFunction<double(int, int)>& x) {
return !!x;
}
int main() {
SmallFunction<void(int)> f = [j = 5](int i) { std::cout << i + j; };
f(42);
SmallFunction<void(int)> g = [](int i) { std::cout << i; };
g(99);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment