11# Binding API inputs to storage with ` folly/lang/bind/ `
22
3- ## User guide
3+ ## For users
44
55Are you trying to call a ` folly::bind ` -enabled API? For simple usage,
66you should not need to read this file at all! Read the API docs instead.
@@ -9,37 +9,52 @@ NEVER write functions that pass `folly::bind` helper types (`constant`,
99` const_ref ` , ` args ` , etc) by reference. These immovable objects must
1010only exist in the statement that constructed them.
1111
12- The high-level idea of ` folly::bind ` is to offer ** the caller ** a
13- vocabulary to describe the storage types to be used by the callee.
12+ Does ` folly::bind::some_word ` feel too long? It is fine to write this in any
13+ ` .cpp ` file, or inside your project-level namespace:
1414
15- For example, if a callee wants to store a generic tuple, the caller may
16- write ` constant{5}, const_ref{b, c}, mut_ref{d} ` to define the storage as
17- ` std::tuple<const int, const int&, const int&, int&>{5, b, c, d} ` .
15+ namespace bind = folly::bind;
16+
17+ The high-level idea of ` folly::bind ` is to offer ** the caller** a vocabulary to
18+ describe the storage types to be used by the callee.
19+
20+ For example, if ` foo() ` wants to store a generic tuple, its caller may write:
21+
22+ foo(bind::constant{5}, bind::const_ref{b, c}, bind::mut_ref{d})
23+
24+ That tells ` foo() ` to store:
25+
26+ std::tuple<const int, const int&, const int&, int&>{5, b, c, d}
1827
1928Your specific API's docs are authoritative -- a callee can change modifier
2029meanings, or define new ones. That said, suggested modifier semantics are:
30+
2131 - Pass non-movable, non-copyable types via ` bind::in_place<T>() ` or
2232 ` bind::in_place_with(fn) ` .
23- - The API decides whether ` const_ref ` / ` mut_ref ` are supported.
33+
34+ - The API decides whether to support ` bind::const_ref ` / ` bind::mut_ref ` .
2435 If NOT, then the callee's signature decides between by-value & by-ref.
25- * Using ` const_ref ` etc should cause a compile error (` static_assert ` ).
26- If YES, they tell the callee to take the argument by reference. Then:
27- * WATCH OUT: ` const_ref ` / ` mut_ref ` is explicit unlike regular C++.
36+ * Using ` bind::const_ref ` etc should be a compile error (` static_assert ` ).
37+ If YES, these tell the callee to take the argument by reference.
38+ * WATCH OUT: Unlike plain C++, ` bind::const_ref ` / ` bind::mut_ref ` is
39+ explicit about ` const ` -ness.
40+
2841 Rationale: Programming best practice is to minimize "implicit"
2942 mutation. When writing modern C++, two argument passing styles
3043 predominate: ` T ` -- you own this, and ` const T& ` -- a read-only
3144 input. Any mutable "output" references, like ` T& ` , ought to be
3245 plainly visible at the callsite to avoid bugs, and ` mut_ref ` is.
46+
3347 * Passing a variable without modifiers will be pass-by-value.
48+
3449 - Unlike ` std::as_const ` , which changes the ` const ` ness of the input,
3550 ` bind::constant ` / ` bind::mut ` say whether the * destination* is ` const ` .
3651 So, ` constant ` is like ` const T var ` in the callee's signature. E.g.
37- * ` constant(std::move(var )) ` moves in the value , and stores it as ` const ` .
38- * ` mut ` never removes a ` const ` qualifier from the underlying
39- data. Rather, it can override the "references default to ` const ` "
40- behavior, or to override a ` constant ` modifier that it surrounds.
52+ * ` bind:: constant(std::move(v ))` moves in ` v ` , and stores it as ` const ` .
53+ * ` bind:: mut` never removes a ` const ` qualifier from the underlying data.
54+ It can override the "references default to ` const ` " behavior, or a
55+ ` bind:: constant` modifier that it surrounds.
4156
42- ## Library authors only
57+ ## For library authors
4358
4459### What is this for?
4560
@@ -55,8 +70,8 @@ These options have tradeoffs, but none provide ALL of these features:
5570 - Let the caller set the target storage (value or ref, const or not) for
5671 the binding, with the callee just specifying just ` auto ` . Consequences:
5772 * The callee can reflect on the supplied args, without C++26 P2996
58- * ` folly:: bind::constant` lets you move a non-` const ` object into
59- ` const ` storage, while ` std::as_const ` cannot.
73+ * ` bind::constant ` lets you move a non-` const ` object into ` const `
74+ storage, while ` std::as_const ` cannot.
6075 - Define custom binding logic for some args, like ` as_capture ` in
6176 ` async_closure ` , or named arguments in ` folly/lang/named ` .
6277 - Allow helper functions to return several adjacent arguments -- as if
@@ -72,9 +87,15 @@ uniformly bind:
7287
7388Consider this ` folly::bind ` expression:
7489
75- bind::args ba{5, constant{a}, mut_ref{b, std::move(c)}, const_ref{d}};
90+ bind::args ba{
91+ 5,
92+ bind::constant{a},
93+ bind::mut_ref{b, std::move(c)},
94+ bind::const_ref{d}};
7695
77- Stored with the default ` bind_to_storage_policy ` , this is akin to:
96+ With the default ` bind_to_storage_policy ` , it implies the following storage
97+ types (but not the outer ` std::tuple ` -- the algorithm taking ` ba ` must
98+ construct it -- feel free to add ` lang/bind/ToTuple.h ` ).
7899
79100 std::tuple<int, const A, B&, C&&, const D&> tup =
80101 std::forward_as_tuple{5, a, b, std::move(c), d};
@@ -83,7 +104,7 @@ Regular C++ arguments are fine (and preferred!) when the destination types
83104are known in advance, and the types are movable. But, in trickier cases
84105` folly::bind ` saves the day:
85106 - It lets the caller ergonomically declare a structure at the same time it
86- is constructed or passed (` async_closure ` , named scopes).
107+ is constructed or passed (` async_closure ` , ` safe_closure ` , named scopes).
87108 - In order to in-place construct an immovable type ` A ` by-value inside the
88109 caller's storage, you need an implicit conversion operator.
89110 ` bind::in_place* ` implements one on your behalf.
@@ -93,21 +114,25 @@ are known in advance, and the types are movable. But, in trickier cases
93114### What changes in the UX over standard C++ bindings?
94115
95116That depends on how you integrate ` folly::bind ` , but... the recommended
96- way is to use the standard ` bind_to_storage_policy ` , possibly extending it in a
97- careful way. The goal should be "low user surprisal", so we stay close to
117+ way is to use a standard policy, possibly with careful extensions. Namely:
118+ - ` bind_to_storage_policy ` when the callee wants ` bind:: ` verbs to modulate
119+ how the ` bind::args ` are stored -- see the ` std::tuple ` example above.
120+ - ` bind_as_argument ` when the callee wants ` bind:: ` verbs to change how
121+ its internally-stored values are bound to a function argument, as in
122+ ` safe_closure() ` .
123+
124+ The goal should be "low user surprisal", so the standard policies stay close to
98125standard C++ semantics, except for a couple of restrictions to make argument
99126passing less bug-prone.
100127
101- With the vanilla ` bind_to_storage_policy ` , besides ` bind::in_place* ` support, you get:
102- - Arguments are bound by value, unless the callsite includes ` const_ref ` /
103- ` mut_ref ` . This prevents bugs from callers not expecting aliasing.
104- - While ` copy{} ` and ` move{} ` modifiers aren't yet provided, ` category_t `
105- and ` bind_to_storage_policy ` allow for the notion of the caller restricting that
106- an argument be passed by copy, or move-copy.
128+ For example, with the standard ` bind_to_storage_policy ` , you get
129+ ` bind::in_place* ` support, you and this unsurprising behavior:
130+ - Arguments are stored as values, unless the callsite has ` bind::const_ref ` /
131+ / ` bind::mut_ref ` . This prevents bugs from callers not expecting aliasing.
107132
108133In "synchronous" APIs, where your code only runs while the user's original
109- statement is active, another viable integration is to define a policy that
110- defaults to bind-by-reference. This policy could be added to ` Bind .h` .
134+ statement is active, you may want a policy that defaults to bind-by-reference.
135+ For this usage, take a look at ` AsArgument .h` .
111136
112137### To integrate ` Bind.h ` , take ` bind::args ` via CTAD in an immovable class
113138
@@ -119,7 +144,7 @@ void foo(bind::args<T> args) {
119144 // Read "Using your bound args" for how to access ` args `
120145}
121146// User code
122- foo(bind::args{5, constant(bind::in_place<Bar >(x), std::move(y))});
147+ foo(bind::args{5, bind:: constant(bind::in_place<Bar >(x), std::move(y))});
123148```
124149
125150#### Alternative: API with no user-visible `bind::args`
@@ -129,7 +154,7 @@ Before going down this road, read this section *carefully*. Needing to depend
129154on CTAD limits your template deduction capabilities, and forces you to
130155implement each API method as a class.
131156
132- In `Bind.h`, all the modifiers (like `constant`) look similar to this:
157+ In `Bind.h`, all the modifiers (like `bind:: constant`) look similar to this:
133158
134159```cpp
135160template <typename... Ts>
@@ -158,39 +183,39 @@ example, see the `#if 0` in the test under `all_tests_run_at_build_time`.
158183
159184Notes:
160185 - ` private ` inheritance prevents ` YOUR_TYPE ` from being nested inside
161- binding modifiers like ` constant() ` . You can relax to ` public ` , but
162- only if your type has the inherited ctor shown above, and can logically
163- be thought of as a bag of args.
186+ binding modifiers like ` bind:: constant()` . You can relax to ` public ` , but
187+ only if your type has the inherited ctor shown above, and can logically be
188+ thought of as a bag of args.
164189 - A linter is proposed, but not yet implemented, for detecting cases
165190 where ` Bind.h ` helpers are being taken by-reference. With this linter, the
166191 prvalue-only protection against lifetime bugs will be robust.
167192
168- (2) Prvalue semantics only allows us to take arguments by-value. C++20
169- lacks perfect forwarding for prvalues, and there is not even an accepted
170- proposal for future releases (though P2785 would help). Yet, in generic
171- code, we want to allow packs of arguments where some are binding helpers
172- (like ` constant(5) ` ) and others are perfect-forwarded references (like ` x ` ).
193+ (2) Prvalue semantics only allows us to take arguments by-value. C++20 lacks
194+ perfect forwarding for prvalues, and there is not even an accepted proposal for
195+ future releases (though P2785 would help). Yet, in generic code, we want to
196+ allow packs of arguments where some are binding helpers (like
197+ ` bind:: constant(5)` ) and others are perfect-forwarded references (like ` x ` ).
173198Without prvalue perfect forwarding, the next best trick is to implicitly
174199convert every arg to a ` bind::args<T> ` value, as done by this ctor.
175200 - If the argument type ` T ` derives from ` bind::ext::like_args ` , we wrap it in
176201 ` struct args<T> : T ` , moving the underlying data via the
177202 implementation-detail ` unsafe_move_args ` protocol. This handles the case
178- when the user passes a modifier like ` constant(5) ` as an arg.
203+ when the user passes a modifier like ` bind:: constant(5)` as an arg.
179204 - For all other ` T ` , ` args<T> ` simply captures a forwarding
180205 reference to the input.
181206
182207IMPORTANT:
183- - To be lifetime-safe, this ctor takes the ` args<Ts> ` by-value.
184- - Do ** NOT** assume that ` Ts ` are your input types. A single ` args `
185- may represent a sequence of arguments to bind (e.g. ` constant(5, x) ` ,
186- and the base class ` args<Ts...> ` takes care of flattening these
187- for you. The usage is explained below.
208+ - To be lifetime-safe, this ctor takes the ` bind:: args<Ts>` by-value.
209+ - Do ** NOT** assume that ` Ts ` are your input types. A single ` bind:: args`
210+ may represent a sequence of arguments to bind -- ` bind:: constant(5, x)` --
211+ and the base class ` bind:: args<Ts...>` takes care of flattening these for
212+ you. The usage is explained below.
188213
189214(3) The deduction guide is necessary so that the class's ctor (2) can
190- implicitly convert each argument into a ` args<T> ` value. In other
215+ implicitly convert each argument into a ` bind:: args<T>` value. In other
191216words, it arranges for ` T ` to record the type of the argument as provided.
192217
193- Once the base ` bound_arg <Ts...>` is constructed, you can access the
218+ Once the base ` bind::args <Ts...>` is constructed, you can access the
194219** flattened** bound args:
195220 - The "input" and "storage" types for each argument via the class template
196221 ` bind_to_storage_policy ` , and member type list ` binding_list_t ` .
@@ -216,7 +241,7 @@ There are two natural choices for **when** to run your business logic:
216241 recommendable pattern, since error handling gets trickier (either your
217242 class models both value & error states, or your ctor throws). However,
218243 it can give a simpler UX. Implementing this requires some adjustments:
219- - Stop inheriting from ` args ` , since you would no longer want to
244+ - Stop inheriting from ` bind:: args` , since you would no longer want to
220245 store the bound args tuple, but only the API's result.
221246 - Instead, lightly refactor ` Bind.h ` to expose the argument-flattening
222247 logic by composition.
0 commit comments