/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * 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. */ #pragma once #include #include #include #include #include #include // Utility functions for container implementors namespace folly { namespace detail { template struct TemporaryEmplaceKey { TemporaryEmplaceKey(TemporaryEmplaceKey const&) = delete; TemporaryEmplaceKey(TemporaryEmplaceKey&&) = delete; template TemporaryEmplaceKey(Alloc& a, std::tuple&& args) : alloc_(a) { auto p = &value(); apply( [&, p](auto&&... inner) { std::allocator_traits::construct( alloc_, p, std::forward(inner)...); }, std::move(args)); } ~TemporaryEmplaceKey() { std::allocator_traits::destroy(alloc_, &value()); } KeyType& value() { return *static_cast(static_cast(&raw_)); } Alloc& alloc_; std::aligned_storage_t raw_; }; // A map's emplace(args...) function takes arguments that can be used to // construct a pair, but that construction // only needs to take place if the key is not present in the container. // callWithExtractedKey helps to handle this efficiently by looking for a // reference to the key within the args list. If the search is successful // then the search can be performed without constructing any temporaries. // If the search is not successful then callWithExtractedKey constructs // a temporary key_type and a new argument list suitable for constructing // the entire value_type if necessary. // // callWithExtractedKey(a, f, args...) will call f(k, args'...), where // k is the key and args'... is an argument list that can be used to // construct a pair of key and mapped value. Note that this means f gets // the key twice. // // In some cases a temporary key must be constructed. This is accomplished // with std::allocator_traits<>::construct, and the temporary will be // destroyed with std::allocator_traits<>::destroy. Using the allocator's // construct method reduces unnecessary copies for pmr allocators. // // callWithExtractedKey supports heterogeneous lookup with the UsableAsKey // template parameter. If a single key argument of type K is found in // args... then it will be passed directly to f if it is either KeyType or // if UsableAsKey>::value is true. If you don't care // about heterogeneous lookup you can just pass a single-arg template // that extends std::false_type. template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename Arg1, typename... Args2, std::enable_if_t< std::is_same, KeyType>::value || UsableAsKey>::value, int> = 0> auto callWithExtractedKey( Alloc&, Func&& f, std::piecewise_construct_t, std::tuple&& first_args, std::tuple&& second_args) { // we found a usable key in the args :) auto const& key = std::get<0>(first_args); return f( key, std::piecewise_construct, std::tuple(std::move(first_args)), std::tuple(std::move(second_args))); } template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename... Args1, typename... Args2> auto callWithExtractedKey( Alloc& a, Func&& f, std::piecewise_construct_t, std::tuple&& first_args, std::tuple&& second_args) { // we will need to materialize a temporary key :( TemporaryEmplaceKey key( a, std::tuple(std::move(first_args))); return f( const_cast(key.value()), std::piecewise_construct, std::forward_as_tuple(std::move(key.value())), std::tuple(std::move(second_args))); } template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func> auto callWithExtractedKey(Alloc& a, Func&& f) { return callWithExtractedKey( a, std::forward(f), std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename U1, typename U2> auto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) { return callWithExtractedKey( a, std::forward(f), std::piecewise_construct, std::forward_as_tuple(std::forward(x)), std::forward_as_tuple(std::forward(y))); } template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename U1, typename U2> auto callWithExtractedKey(Alloc& a, Func&& f, std::pair const& p) { return callWithExtractedKey( a, std::forward(f), std::piecewise_construct, std::forward_as_tuple(p.first), std::forward_as_tuple(p.second)); } template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename U1, typename U2> auto callWithExtractedKey(Alloc& a, Func&& f, std::pair&& p) { return callWithExtractedKey( a, std::forward(f), std::piecewise_construct, std::forward_as_tuple(std::move(p.first)), std::forward_as_tuple(std::move(p.second))); } // callWithConstructedKey is the set container analogue of // callWithExtractedKey template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename Arg, std::enable_if_t< std::is_same, KeyType>::value || UsableAsKey>::value, int> = 0> auto callWithConstructedKey(Alloc&, Func&& f, Arg&& arg) { // we found a usable key in the args :) auto const& key = arg; return f(key, std::forward(arg)); } template < typename KeyType, template class UsableAsKey, typename Alloc, typename Func, typename... Args> auto callWithConstructedKey(Alloc& a, Func&& f, Args&&... args) { // we will need to materialize a temporary key :( TemporaryEmplaceKey key( a, std::forward_as_tuple(std::forward(args)...)); return f(const_cast(key.value()), std::move(key.value())); } } // namespace detail } // namespace folly