/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side 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 Server Side 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. */ #include "mongo/util/functional.h" #include "mongo/unittest/unittest.h" namespace { template struct RunDetection { ~RunDetection() { itRan = false; } RunDetection(const RunDetection&) = delete; RunDetection& operator=(const RunDetection&) = delete; RunDetection() { itRan = false; } static bool itRan; }; template bool RunDetection::itRan = false; TEST(UniqueFunctionTest, construct_simple_unique_function_from_lambda) { // Implicit construction { RunDetection<0> runDetection; mongo::unique_function uf = [] { RunDetection<0>::itRan = true; }; uf(); ASSERT_TRUE(runDetection.itRan); } // Explicit construction { RunDetection<0> runDetection; mongo::unique_function uf{[] { RunDetection<0>::itRan = true; }}; uf(); ASSERT_TRUE(runDetection.itRan); } } TEST(UniqueFunctionTest, assign_simple_unique_function_from_lambda) { // Implicit construction RunDetection<0> runDetection; mongo::unique_function uf; uf = [] { RunDetection<0>::itRan = true; }; uf(); ASSERT_TRUE(runDetection.itRan); } TEST(UniqueFunctionTest, reassign_simple_unique_function_from_lambda) { // Implicit construction RunDetection<0> runDetection0; RunDetection<1> runDetection1; mongo::unique_function uf = [] { RunDetection<0>::itRan = true; }; uf = [] { RunDetection<1>::itRan = true; }; uf(); ASSERT_FALSE(runDetection0.itRan); ASSERT_TRUE(runDetection1.itRan); } TEST(UniqueFunctionTest, accepts_a_functor_that_is_move_only) { struct Checker {}; mongo::unique_function uf = [checkerPtr = std::make_unique()]{}; mongo::unique_function uf2 = std::move(uf); uf = std::move(uf2); } TEST(UniqueFunctionTest, dtor_releases_functor_object_and_does_not_call_function) { RunDetection<0> runDetection0; RunDetection<1> runDetection1; struct Checker { ~Checker() { RunDetection<0>::itRan = true; } }; { mongo::unique_function uf = [checkerPtr = std::make_unique()] { RunDetection<1>::itRan = true; }; ASSERT_FALSE(runDetection0.itRan); ASSERT_FALSE(runDetection1.itRan); } ASSERT_TRUE(runDetection0.itRan); ASSERT_FALSE(runDetection1.itRan); } TEST(UniqueFunctionTest, comparison_checks) { mongo::unique_function uf; // Using true/false assertions, as we're testing the actual operators and commutativity here. ASSERT_TRUE(uf == nullptr); ASSERT_TRUE(nullptr == uf); ASSERT_FALSE(uf != nullptr); ASSERT_FALSE(nullptr != uf); uf = [] {}; ASSERT_FALSE(uf == nullptr); ASSERT_FALSE(nullptr == uf); ASSERT_TRUE(uf != nullptr); ASSERT_TRUE(nullptr != uf); uf = nullptr; ASSERT_TRUE(uf == nullptr); ASSERT_TRUE(nullptr == uf); ASSERT_FALSE(uf != nullptr); ASSERT_FALSE(nullptr != uf); } TEST(UniqueFunctionTest, simple_instantiations) { mongo::unique_function a; mongo::unique_function x = []() -> int { return 42; }; x = []() -> int { return 42; }; } namespace conversion_checking { template using uf = mongo::unique_function; template using sf = std::function; // Check expected `is_convertible` traits (which also checks if this kind of conversion will compile // correctly too. TEST(UniqueFunctionTest, convertability_tests) { // TODO when on C++17, see if the new MSVC can handle these `std::isconvertible` static assertions. #ifndef _MSC_VER // Note that `mongo::unique_function` must never convert to `std::function` in any of the // following cases. // No arguments, return variants // Same return type MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); // Convertible return type MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); // Incompatible return type MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Argument consistency, with return variants // Same return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); // Convertible return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); // Incompatible return type, same arguments MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Extra arguments, with return variants // Same return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Convertible return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Incompatible return type, with extra arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Argument conversions, with return variants // Same return type, Convertible argument MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); // Convertible return type, with convertible arguments MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(std::is_convertible, uf>::value); MONGO_STATIC_ASSERT( !std::is_convertible, sf>::value); MONGO_STATIC_ASSERT( std::is_convertible, uf>::value); MONGO_STATIC_ASSERT( std::is_convertible, uf>::value); // Incompatible return type, with convertible arguments (Not permitted) MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); struct X {}; struct Y {}; // Incompatible argument conversions, with return variants // Same return type MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Convertible return type MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); // Incompatible return type MONGO_STATIC_ASSERT(!std::is_convertible, sf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); MONGO_STATIC_ASSERT(!std::is_convertible, uf>::value); #endif } } // namespace conversion_checking template bool accept(std::function arg, U) { return false; } template >::value, void>::type> bool accept(T arg, U) { return true; } TEST(UniqueFunctionTest, functionDominanceExample) { mongo::unique_function uf = [] {}; ASSERT_TRUE(accept(std::move(uf), nullptr)); } } // namespace