Created
October 3, 2017 14:33
-
-
Save avikivity/f078c45776c491ec5febb664bfa9e388 to your computer and use it in GitHub Desktop.
noncopyable_function.hh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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