Skip to content

Instantly share code, notes, and snippets.

@avikivity
Created October 3, 2017 14:33
Show Gist options
  • Save avikivity/f078c45776c491ec5febb664bfa9e388 to your computer and use it in GitHub Desktop.
Save avikivity/f078c45776c491ec5febb664bfa9e388 to your computer and use it in GitHub Desktop.
noncopyable_function.hh
/*
* This file is open source software, licensed to you under the terms
* of the Apache License, Version 2.0 (the "License"). See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright (C) 2017 ScyllaDB Ltd.
*/
#pragma once
#include <utility>
#include <type_traits>
#include <functional>
namespace seastar {
template <typename Signature>
class noncopyable_function;
template <typename Ret, typename... Args>
class noncopyable_function<Ret (Args...)> {
static constexpr size_t nr_direct = 32;
[[gnu::may_alias]]
union storage {
char direct[nr_direct];
void* indirect;
};
using call_type = Ret (*)(const noncopyable_function* func, Args...);
using move_type = void (*)(noncopyable_function* from, noncopyable_function* to);
using destroy_type = void (*)(noncopyable_function* func);
struct vtable {
const call_type call;
const move_type move;
const destroy_type destroy;
};
private:
storage _storage;
const vtable* _vtable;
private:
static Ret empty_call(const noncopyable_function* func, Args... args) {
throw std::bad_function_call();
}
static Ret empty_move(noncopyable_function* from, noncopyable_function* to) {}
static Ret empty_destroy(noncopyable_function* func) {}
static constexpr vtable _s_empty_vtable = {empty_call, empty_move, empty_destroy};
// Accessor templates select either direct or indirect storage for Func
template <typename Func>
struct direct_accessor {
static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.direct); }
static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.direct); }
};
template <typename Func>
struct indirect_accessor {
static Func* access(noncopyable_function* func) { return reinterpret_cast<Func*>(func->_storage.indirect); }
static const Func* access(const noncopyable_function* func) { return reinterpret_cast<const Func*>(func->_storage.indirect); }
};
template <typename Func, template <class> class Accessor>
struct make_vtable_for {
static Ret call(const noncopyable_function* func, Args... args) {
return (*Accessor<Func>::access(func))(std::forward<Args>(args)...);
}
static void move(noncopyable_function* from, noncopyable_function* to) {
new (Accessor<Func>::access(to)) Func(std::move(*Accessor<Func>::access(from)));
}
static void destroy(noncopyable_function* func) {
Accessor<Func>::access(func)->~Func();
}
static void initialize(Func&& from, noncopyable_function* to) {
new (Accessor<Func>::access(to)) Func(std::move(from));
}
static constexpr vtable s_vtable = { &make_vtable_for::call, &make_vtable_for::move, &make_vtable_for::destroy };
};
template <typename Func, bool Direct = true>
struct select_vtable_for : make_vtable_for<Func, direct_accessor> {};
template <typename Func>
struct select_vtable_for<Func, false> : make_vtable_for<Func, indirect_accessor> {};
template <typename Func>
static constexpr bool is_direct() {
return sizeof(Func) <= nr_direct && alignof(Func) <= alignof(storage);
}
template <typename Func>
struct vtable_for : select_vtable_for<Func, is_direct<Func>()> {};
public:
noncopyable_function() noexcept : _vtable(&_s_empty_vtable) {}
template <typename Func>
noncopyable_function(Func&& func) noexcept : _vtable(&vtable_for<Func>::s_vtable) {
vtable_for<Func>::initialize(std::move(func), this);
}
template <typename Object, typename... AllButFirstArg>
noncopyable_function(Ret (Object::*member)(AllButFirstArg...)) noexcept : noncopyable_function(std::mem_fn(member)) {}
template <typename Object, typename... AllButFirstArg>
noncopyable_function(Ret (Object::*member)(AllButFirstArg...) const) noexcept : noncopyable_function(std::mem_fn(member)) {}
Ret operator()(Args... args) const {
return _vtable->call(this, std::forward<Args>(args)...);
}
};
template <typename Ret, typename... Args>
template <typename Func, template <class> class Accessor>
const typename noncopyable_function<Ret (Args...)>::vtable noncopyable_function<Ret (Args...)>::template make_vtable_for<Func, Accessor>::s_vtable;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment