/** * Copyright 2018 MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #pragma once #include #include #include "mongo/stdx/type_traits.h" #include "mongo/util/assert_util.h" namespace mongo { template class unique_function; /** * A `unique_function` is a move-only, type-erased functor object similar to `std::function`. * It is useful in situations where a functor cannot be wrapped in `std::function` objects because * it is incapable of being copied. Often this happens with C++14 or later lambdas which capture a * `std::unique_ptr` by move. The interface of `unique_function` is nearly identical to * `std::function`, except that it is not copyable. */ template class unique_function { private: // `TagTypeBase` is used as a base for the `TagType` type, to prevent it from being an // aggregate. struct TagTypeBase { protected: TagTypeBase() = default; }; // `TagType` is used as a placeholder type in parameter lists for `enable_if` clauses. They // have to be real parameters, not template parameters, due to MSVC limitations. class TagType : TagTypeBase { TagType() = default; friend unique_function; }; public: using result_type = RetType; ~unique_function() noexcept = default; unique_function() = default; unique_function(const unique_function&) = delete; unique_function& operator=(const unique_function&) = delete; unique_function(unique_function&&) noexcept = default; unique_function& operator=(unique_function&&) noexcept = default; void swap(unique_function& that) noexcept { using std::swap; swap(this->impl, that.impl); } friend void swap(unique_function& a, unique_function& b) noexcept { a.swap(b); } // TODO: Look into creating a mechanism based upon a unique_ptr to `void *`-like state, and a // `void *` accepting function object. This will permit reusing the core impl object when // converting between related function types, such as // `int (std::string)` -> `void (const char *)` template /* implicit */ unique_function( Functor&& functor, // The remaining arguments here are only for SFINAE purposes to enable this ctor when our // requirements are met. They must be concrete parameters not template parameters to work // around bugs in some compilers that we presently use. We may be able to revisit this // design after toolchain upgrades for C++17. std::enable_if_t::value, TagType> = makeTag(), std::enable_if_t::value, TagType> = makeTag(), std::enable_if_t::value, TagType> = makeTag()) : impl(makeImpl(std::forward(functor))) {} unique_function(std::nullptr_t) noexcept {} RetType operator()(Args... args) const { invariant(static_cast(*this)); return impl->call(std::forward(args)...); } explicit operator bool() const noexcept { return static_cast(this->impl); } // Needed to make `std::is_convertible, std::function<...>>` be // `std::false_type`. `mongo::unique_function` objects are not convertible to any kind of // `std::function` object, since the latter requires a copy constructor, which the former does // not provide. If you see a compiler error which references this line, you have tried to // assign a `unique_function` object to a `std::function` object which is impossible -- please // check your variables and function signatures. // // NOTE: This is not quite able to disable all `std::function` conversions on MSVC, at this // time. template operator std::function() const = delete; private: // The `TagType` type cannot be constructed as a default function-parameter in Clang. So we use // a static member function that initializes that default parameter. static TagType makeTag() { return {}; } struct Impl { virtual ~Impl() noexcept = default; virtual RetType call(Args&&... args) = 0; }; // These overload helpers are needed to squelch problems in the `T ()` -> `void ()` case. template static void callRegularVoid(const std::true_type isVoid, Functor& f, Args&&... args) { // The result of this call is not cast to void, to help preserve detection of // `[[nodiscard]]` violations. f(std::forward(args)...); } template static RetType callRegularVoid(const std::false_type isNotVoid, Functor& f, Args&&... args) { return f(std::forward(args)...); } template static auto makeImpl(Functor&& functor) { struct SpecificImpl : Impl { explicit SpecificImpl(Functor&& func) : f(std::move(func)) {} RetType call(Args&&... args) override { return callRegularVoid(std::is_void(), f, std::forward(args)...); } std::decay_t f; }; return std::make_unique(std::move(functor)); } std::unique_ptr impl; }; template bool operator==(const unique_function& lhs, std::nullptr_t) noexcept { return !lhs; } template bool operator!=(const unique_function& lhs, std::nullptr_t) noexcept { return static_cast(lhs); } template bool operator==(std::nullptr_t, const unique_function& rhs) noexcept { return !rhs; } template bool operator!=(std::nullptr_t, const unique_function& rhs) noexcept { return static_cast(rhs); } } // namespace mongo