BDE 4.14.0 Production release
|
Macros | |
#define | BDLF_BIND_PARAMINDEX(N) |
#define | BDLF_BIND_EVAL(N) |
Provide a signature-specific function object (functor).
This component provides a parameterized binder mechanism, bdlf::Bind
, that is a functor object that binds an invocable object or function to a list of arguments. This component also defines factory methods in the bdlf::BindUtil
namespace for creating bdlf::Bind
objects (e.g., bind
and bindR
) and bdlf::BindWrapper
objects (e.g., bindS
and bindSR
). The bdlf::Bind
functor (called henceforth a "binder") is an object that can hold any invocable object (the "bound functor") and a number of parameters (the "bound arguments", some of which can be place-holders of type bdlf::PlaceHolder
). When the binder is later invoked (with optional additional arguments called the "invocation
arguments" used to compute the value of bound arguments that use place-holders), it returns the result of calling the bound functor with the bound arguments and invocation arguments. The bdlf::BindWrapper
functor provides a binder with shared pointer semantics to a non-modifiable binder (both operator*
and operator->
accessors) and forwards its construction arguments to the underlying binder. The section "Supported functionality
and limitations" details which kind of bound functors and bound arguments can be used in conjunction with this component.
The main use of bdlf::Bind
is to invoke bound functors with additional data that is not available prior to the point the functor is invoked (as is often the case with callback functions). The binding is accomplished by passing place-holders instead of literal values as the bound arguments. When the binder is invoked, these place-holders are replaced by their respective invocation arguments. The section "Elementary construction and
usage of bdlf::Bind objects" shows an elementary (but not realistic) usage of the bdlf::BindUtil::bind
factory methods with free functions, that should be enough for most users to grasp the basic usage of this component. The section "Binding data" offers more details and should enable a user to make a more advanced usage of this component. The usage example presents many uses of bdlf::Bind
and bdlf::BindUtil
in a somewhat realistic (but a bit more involved) situation.
Note that bdlf::Bind
functors are typically used with standard algorithms, or with bsl::function
. This mechanism is similar to, but much more powerful than, bsl::bind1st
or bsl::bind2nd
.
The difference between a binder created using one of bindS
and bindSR
and a binder created using bind
, and bindR
is that in the former case the binder is returned by reference rather than by value, with shared ownership semantics. Hence its main use is for creating binders that hold a non-trivial amount of storage (i.e., the bound arguments) and will be copied, possibly several times, such as jobs enqueued in a threadpool.
An invocable object is any object that can be invoked in syntactically the same manner as a function. Invocable objects include function pointers, and objects that provide an operator()
method. This component supports bound objects that can be function pointers, member function pointers (the first bound argument must evaluate to an instance of the class of which the function is a member), or function objects passed by address or by value. In addition, there is a limitation on the number of parameters that such an object can take (currently no more than 14).
A bdlf::Bind
functor can be constructed, usually by one of the bdlf::BindUtil
factory methods, from a bound functor and from 0 up to 14 bound arguments that can be either literal values, place-holders of type bdlf::PlaceHolder
, or further bdlf::Bind
functors. The type of a binder object is a complicated expression, which is why a binder is typically a short-lived object that is returned or passed as parameter to a function template. That is also why the bdlf::BindUtil
factory methods are the preferred way to create a binder. When a binder is later invoked with some arguments, literal values are passed to the bound functor and place-holders are substituted by their respective invocation arguments. In addition, any argument that is a binder itself is invoked recursively with the same invocation arguments and the result of that invocation is passed to the parent binder. The section "Elementary construction and usage of
bdlf::Bind objects" below details the whole mechanism and offers some examples.
The bdlf::Bind
functors support bslma::Allocator *
arguments. When binders are constructed by the bdlf::BindUtil::bind
(and bindR
) factory methods, the currently installed default allocator is used. When binders are constructed by the bdlf::BindUtil::bindS
(and bindSR
) factory methods, the non-optional, user-supplied allocator is used both for the creation of the bound functor arguments and for the reference counting mechanism that manages those arguments. See the section "Binding with
allocators" below for a more detailed discussion.
Bound functors are generally constructed by invoking the bdlf::BindUtil
with an "invocation template". An invocation template is a series of one or more arguments that describe how to invoke the bound functor. Each argument can be either a place-holder or a literal value. Literal values are stored by value in the binder and directly forwarded to the bound functor when invoked. Place-holders are substituted with the respective argument provided at invocation of the binder. For example, given the following invocable
(here a free function for simplicity):
and the following (global) string:
we can bind the parameters of invocable
to the following arguments:
and the binder declared above can be passed invocation arguments directly, as follows (here we specify zero invocation arguments since all the bound arguments are fully specified):
Similarly, we can also create a reference-counted shared binder using the bindS
method:
In the function call above, the invocable
will be bound with the arguments 10
, 14
, and "p3"
respectively, then invoked with those bound arguments. In the next example, place-holders are used to forward user-provided arguments to the bound functor. We separate the invocation of the binder into a function template to avoid having to declare the type of the binder:
The creation of the binder is as follows:
In this code snippet, the callBinder
template function is invoked with a binder bound to the specified invocable
and having the invocation template _1
, _2
, and "p3"
respectively. The two special parameters _1
and _2
are place-holders for arguments one and two, respectively, which will be specified to the binder at invocation time. Each place-holder will be substituted with the corresponding positional argument when invoked. When called within the callBinder
function, invocable
will be invoked as follows:
Place-holders can appear anywhere in the invocation template, and in any order. The same place-holder can appear multiple times. Each instance will be substituted with the same value. For example, in the following snippet of code, the callBinder
function, is invoked with a binder such that argument one (10) of the binder is passed as parameter two and argument two (14) is passed as (i.e., bound to) parameter one:
When called within the callBinder
function, invocable
will be invoked as follows:
The following snippet of code illustrates a number of ways to call bdlf::BindUtil
and their respective output:
The usage example below provides a more comprehensive series of calling sequences.
The main use of bdlf::Bind
is to invoke bound functors with additional data that is not available prior to the point the functor is invoked (as is often the case with callback functions). For that purpose, place-holders are key. There are a couple of issues to understand in order to properly use this component. The bound arguments must be of a value-semantic type (unless they are place-holders or bdlf::Bind
objects). They are evaluated at binding time once and only once and their value copied into the binder (using the default allocator to supply memory unless an allocator is specified). A bdlf::Bind
object always invokes its bound functor with only the arguments listed as bound arguments, regardless of how many arguments are specified to the binder at invocation time. Invocation arguments that are not referenced through a place-holder are simply discarded. Invocation arguments that are duplicated (by using the same place-holder several times) are simply copied several times. The following examples should make things perfectly clear.
It is possible to pass more invocation arguments to a binder than were specified in the signature by the number of bound arguments. Invocation arguments not referenced by any placeholder, as well as extra invocation arguments, will be ignored. Note that they will nevertheless be evaluated even though their value will be unused. Consider, for example, the following snippet of code:
In the above snippets of code, singleArgumentFunction
will be called with only the second argument (14) specified to the binder at invocation time in the callBinderWithSideEffects1
function. Thus the return value of the invocation must be 14. The identityFunctionWithSideEffects(10)
will be evaluated, even though its return value (10) will be discarded. We can check this as follows:
We repeat the same call using bindS
below:
A place-holder can be specified multiple times, effectively passing the same value to different arguments of the function. The value will be evaluated only once. To illustrate this, consider another example that reuses the singleArgumentFunction
of the previous example:
In the above snippet of code, doubleArgumentFunction
will be called with the first argument (identityFunctionWithSideEffects(10)
) specified to the binder, computed only once at invocation time. We can check this as follows:
We repeat the same call using bindS
below:
There are a few issues to be aware of concerning the kind of bound functors that can successfully be used with this component.
Although member function pointers are not invoked in syntactically the same manner as free functions, they can still be used directly in conjunction with bdlf::Bind
. When the binder detects that a member function pointer was specified, it automatically wraps it in a bdlf::MemFn
object. In this case a pointer to the object must be passed as the first argument to bind, followed by the remaining arguments. See the usage example "Binding to
Member Functions" below.
Note that special care should be exercised when passing this
to bind. If this
is passed as the first argument to bind, then all other arguments must be passed by value or by const reference.
It is possible to create a binder with a free function (pointer or reference) that takes an ellipsis (e.g., int printf(const char*, ...
). This component does not support ellipsis in member function pointers, however. See the bindTest7
example function at the end of the usage example below.
Although function objects are invoked in syntactically the same manner as free functions, they can be used by value or by address in conjunction with bdlf::Bind
. When the binder detects that a pointer to a function object was specified, it automatically dereferences that pointer prior to invoking the function object. The difference between the two usages is that the binder object holds a copy of the whole object or of its address only. In particular, when passing by value an object that takes an allocator, the copy held by the binder uses the default allocator if constructed by bdlf::BindUtil::bind
or bdlf::BindUtil::bindR
, not the allocator of the original object.
For keeping the same allocator, pass the object by address to the binder, or call bindS
or bindSR
instead. See the section "Binding with allocators" and the usage example sections "Binding to Function Objects" and "Binding to
Function Objects by Reference" below.
CAVEAT: When passing a function object by value, only the (non-modifiable) copy held by the binder will be invoked. Prior to this version, it was possible to modifiably invoke this copy (hence a non-const
operator()
) with the intent to modify the state of the function object. However, only the copy held by the binder was modified and the original function object passed to the binder was not, but this usage error went undetected. In this version, a binder cannot modifiably invoke functors held by value, and attempting to do so will trigger a compilation error. If it is desired that an invocation modifies the state of the function object, then this function object must be passed to the binder by address.
When binding a function pointer or class method pointer, the pointer passed must unambiguously refer to a a single overload. When binding a functor object, it is possible for the choice of method overload called to be deferred until invocation.
A bdlf::Bind
object will strive to properly and automatically resolve the signature of its bound functor between different overloads of that invocable. The signature of the bound functor is inferred from that of the bound functor and the type of the arguments either at binding or invocation time. The signature of invocables that are not function objects (i.e., free functions with C++ linkage and member functions) must be determined at binding time (in particular, overloads must be disambiguated when obtaining the address of the function). In those cases, the bound arguments will be of the corresponding type and any values passed as bound arguments (except placeholders) will be cast to the corresponding type at binding time and stored by value unless the argument type is a reference type.
Invocation arguments will be cast in place of their corresponding place-holder(s) to the corresponding type only at invocation time. If the signature of the bound functor is known, the invocation argument will be forwarded using the most efficient type (in particular, unnecessary copies will be avoided for non fundamental types).
Some invocable objects, however, may not allow the binder to detect their signature until invocation. This is the case for function objects, for free functions with C linkage (e.g., printf
), if a bound argument is a nested binder, or if a placeholder is used in two positions in the bound arguments. In that case, the bound arguments are stored in their own types, and cast to the corresponding argument type of the signature only when the signature is determined at invocation time. Place-holders, likewise, are not typed and will acquire the type of their corresponding invocation argument when invoked, which will be cast to the corresponding argument type of the signature. In particular, the same binder constructed with a functor and place-holders in place of the bound arguments can invoke several overloads of the operator()
of the functor, depending on the type of the invocation arguments.
Although function objects are invoked in syntactically the same manner as free functions, their return type cannot always be inferred. The same limitation applies to free functions with extern "C"
linkage. In that case, the return type has to be given explicitly to the binder. This can be done by using the bdlf::BindUtil::bindR
function. Note that all bsl::function
objects have a standard public type result_type
to assist the deduction of return type and can be used with bdlf::BindUtil::bind
. See the usage example "Binding to a Function Object with Explicit Return
Type" below.
Due to a technical restriction of the C++ language known as the "forwarding
problem", it is not possible to match the signature of a function object exactly when passing a mix of non-const
lvalues and rvalues as invocation arguments. Nevertheless, this component supports passing literal values and temporaries as invocation arguments to a bdlf::Bind
object. There is however one limitation: if any of the arguments in the signature of the bound functor should be of a modifiable reference type, then all the invocation arguments need to be modifiable references. That is, it is not possible to pass a literal (const
) value to some argument of a bound functor when another argument expects a modifiable reference. Note that a direct call to the bound functor (without the binder) would accept such an argument. This is not a severe limitation, and the workaround is to pass instead a local modifiable variable initialized to the literal value.
The bound functor and bound arguments are created as members of the bdlf::Bind
object, so no memory is allocated for storing them. However, if the bound functor or bound argument's copy constructor requires memory allocation, that memory is supplied by the currently installed default allocator unless bdlf::BindUtil::bindS
(or bindSR
) method is used. In the latter cases, the non-optional, user-supplied allocator is passed to the copy constructors of the bound functor and arguments.
When invoking a bound functor object, the (unbound) arguments are passed "as is" to the bound functor. Those arguments are not copied if the bound functor takes them by modifiable or non-modifiable reference.
In order to make clear where the allocation occurs, we will wrap "p3" into a type that takes an allocator, e.g., a class MyString
(kept minimal here for the purpose of exposition):
We will also use a bslma::TestAllocator
to keep track of the memory allocated:
To expose that the default allocator is not used, we will use a default allocator guard, which will re-route any default allocation to the local defaultAllocator
:
We now create a shared binder object with allocator
using bindS
:
When the bound object is an instance of a class taking an allocator, then allocator
is be passed to its copy constructor as occurs in this example. Here allocator
is used to make the copy of myString
held by the binder.
We now check that memory was allocated from the test allocator, and none from the default allocator:
This section illustrates intended use of this component.
What follows is a series of code snippets illustrating detailed aspects of typical usage of bdlf::Bind
and bdlf::BindUtil
. For these examples, we will use a typical pair of event and event dispatcher classes, where the event is defined as:
and the event dispatcher is defined as follows:
A dispatcher is created with a callback function object as follows:
and its main function is to invoke the callback on the series of events as obtained by getNextEvent
:
We illustrate how to use the dispatcher with free callback functions that have various signatures by passing a binder as the callback function of the dispatcher, and how to use the binder to match the signature of the callback function. Note that at the point of invocation in dispatch
the binder will be invoked with two invocation arguments, thus we may use only place-holders _1
and _2
. In the following snippet of code, the binder passes its invocation arguments straight through to the callback:
Next we show how to bind some of the callback arguments at binding time, while letting the invocation arguments straight through to the callback as the first two arguments:
In the next snippet of code, we show how to reorder the invocation arguments before they are passed to the callback:
And finally, we illustrate that the signature of the callback can be smaller than expected by the dispatcher by letting the binder ignore its first argument:
In the next example, we wrap the callback function into a function object which is bound by value. For brevity, we only present the basic example of passing the arguments straight through to the actual callback operator()
, but all the variations of the previous example could be given as well.
The following example reuses the MyCallbackObject
of the previous example, but illustrates that it can be passed by reference as well as by value:
When passed by reference, only the address of the function object is copied. Hence the function object must remain valid throughout the lifetime of the binder.
In the next example, we show that the callback function can be a member function, in which case there are three, not two, bound arguments. The first bound argument must be a pointer to an instance of the class owning the member function.
We now show that it is possible to provide a binder as an argument to bdlf::BindUtil
. Upon invocation, the invocation arguments are forwarded to the nested binder.
When the return type cannot be inferred from the bound functor (using typename t_FUNC::ResultType
), the binder needs an explicit specification. This is done by using the bdlf::BindUtil::bindR
function template as exemplified below:
Another situation where the return type (in fact, the whole signature) cannot be inferred from the bound functor is the use of the free function with C linkage and variable number of arguments printf(const char*, ...)
. In the following code snippet, we show how the argument to the callBinder
function (redefined below for the reader's convenience) of section "Elementary construction and usage of bdlf::Bind objects" above can be bound to printf
:
When called, bindTest7
will create a binder, pass it to callBinder
which will invoke it with arguments 10
and 14
, and the output will be:
#define BDLF_BIND_EVAL | ( | N | ) |
#define BDLF_BIND_PARAMINDEX | ( | N | ) |