/* * Copyright (c) Meta Platforms, Inc. and 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 /// /// Extends `lang/Bindings.h` (read that first) with keyword-argument syntax: /// bound_args{ /// self_id = make_in_place(), // tag akin to Python's `self` /// "x"_id = 5, /// "y"_id = mut_ref{y}, /// } /// /// If the API takes a pack of named bindings, the above syntax is all that a /// callers needs to know. Some API mays require you to wrap the pack with a /// custom container like `named_values{...}` (not yet built). /// namespace folly::bindings { namespace ext { // For extension authors -- public details template Base> struct named_bind_info_t : Base { constexpr const Base& as_base() const { return *this; } using Base::Base; constexpr explicit named_bind_info_t( // Use a constraint to prevent object slicing. std::same_as auto b) : Base(std::move(b)) {} }; namespace detail { template struct named_bind_info { template requires( // Use a constraint to prevent object slicing. std::derived_from // XXX Future: && a clause to prevent chaining assignments. ) constexpr named_bind_info_t operator()(BI bi) const { return named_bind_info_t{std::move(bi)}; } }; } // namespace detail template struct id_arg : ext::merge_update_bound_args, T> { using ext::merge_update_bound_args, T>:: merge_update_bound_args; }; struct self_id_t {}; template struct self_id_arg : ext::merge_update_bound_args, T> { using ext::merge_update_bound_args, T>:: merge_update_bound_args; }; template // `literal_string<"x">` or `self_id_t{}` class identifier { private: template static constexpr id_arg make_with_tag(vtag_t, auto&& ba) { return id_arg{bound_args_unsafe_move::from(std::move(ba))}; } template static constexpr self_id_arg make_with_tag( vtag_t, auto&& ba) { return self_id_arg{bound_args_unsafe_move::from(std::move(ba))}; } public: // "x"_id = some_binding_modifier{var} constexpr auto operator=( std::derived_from auto ba) const { [](tag_t) { static_assert( sizeof...(BTs) == 1, "Cannot give the same name to multiple bound args."); }(typename decltype(ba)::binding_list_t{}); return make_with_tag(vtag, std::move(ba)); } // "x"_id = var template requires(!std::derived_from) constexpr auto operator=(T&& t) const { return make_with_tag(vtag, bound_args{static_cast(t)}); } static inline constexpr auto folly_bindings_identifier_tag = Tag; }; } // namespace ext // These types represents "identifier tag -> storage type" signatures for // bindings. Library authors: search for `signature_type`. // // NB: `id_type` is distinct from `self_id_type` for cleaner error messages. // Logically, both of these are `tag_type` (or similar), and the // implementation could later be generalized, but for now, this is ok. template struct self_id_type { using identifier_type = ext::identifier; using storage_type = UnderlyingSig::storage_type; }; template struct id_type { using identifier_type = ext::identifier; using storage_type = UnderlyingSig::storage_type; }; // For brevity, `identifier`s are usually made via custom string literals. inline namespace literals { inline namespace string_literals { template consteval auto operator""_id() noexcept { return ext::identifier{}; } } // namespace string_literals } // namespace literals // A special `identifier` tag, which works just like `"x"_id`. It's meant as // syntax sugar for "self-reference" scenarios, such as: // - Tagging for the implicit scope/object parameter in `spawn_self_closure`. // - Tagging `this`-pointer-like objects when calling class member functions // with a kwarg calling convention. // While it somewhat resembles the role of the `this` pointer, the name // `this_id` is ambiguous (which ID?), and doesn't sound well in all use-cases. inline constexpr ext::identifier self_id; namespace ext { struct no_tag_t {}; template inline constexpr no_tag_t named_bind_info_tag_v{}; template inline constexpr auto named_bind_info_tag_v> = Tag; // Named bindings follow the binding policy of the underlying base template // Use a constraint to avoid object slicing requires(!std::is_same_v< const no_tag_t, decltype(named_bind_info_tag_v)>) class binding_policy> { private: using underlying = binding_policy>; template static constexpr auto signature_type_for( const named_bind_info_t&) -> self_id_type; template static constexpr auto signature_type_for(const named_bind_info_t&) -> id_type; public: using storage_type = typename underlying::storage_type; using signature_type = decltype(signature_type_for(NBI)); }; } // namespace ext } // namespace folly::bindings