Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslmf_forwardingtype
[Package bslmf]

Provide a meta-function for determining an optimal forwarding type. More...

Namespaces

namespace  bslmf

Detailed Description

Outline
Purpose:
Provide a meta-function for determining an optimal forwarding type.
Classes:
bslmf::ForwardingType meta-function to determine optimal forwarding type
bslmf::ForwardingTypeUtil Namespace for forwarding functions
See also:
Component bslmf_removecvq
Description:
This component, while not deprecated, is largely **superseded** by the simpler bslmf_forwardingreftype. Although there may be use cases for which bslmf_forwardingtype is to be prefered, new users are encouraged to consider bslmf_forwardingreftype and understand the differences between the two components (see Comparison to bslmf_forwardingreftype).
This component provides a meta function, bslmf::ForwardingType, determining the most efficient forwarding type for a given template type t_TYPE. The forwarding type is used to pass an argument from the client of a component through a chain of nested function calls to the ultimate consumer of the argument. This component also provides a utility class template, bslmf::ForwardingTypeUtil, supplying functions to most efficiently forward an argument to another function.
For instance, basic types (e.g., fundamental types, pointer types, function references and pointers) can efficiently be passed by value down a chain of nested function calls. However a large object or one with a non-trivial copy constructor would be better passed by const reference, even if the ultimate consumer takes that argument by value.
Another form of optimization is the early decay of arrays to pointers, preventing a proliferation of different template instantiations for every array size being used. Although the outermost function may still be instantiated on the full array type, intermediate functions are all instantiated on the same pointer type, regardless of array size. This decay also applies to reference-to-array types. The user can recover the original array type when forwarding to the final consumer by using bslmf::ForwardingTypeUtil<T>forwardToTarget() (see below).
An argument v of type T can be passed as type ForwardingType<T>Type down an arbitrarily-long chain of function calls without ever calling std::forward. However, in order to avoid an extra copy as well as to select the correct overload and instantiation of the eventual target function, it should be converted back to a type that more closely resembles the original T by calling ForwardingTypeUtil<T>forwardToTarget(v).
This component is intended to be used when performance is of highest concern or when creating function wrappers that are intended to minimize perturbations on the interface of the functions that they wrap.
Note that previous versions of this component forwarded const references to basic types by value instead of by const reference. This transformation was an attempt to avoid an extra dereference operation, but in real use cases the extra dereference happened anyway, on the call to the outermost forwarding function. Moreover, such a transformation is subtly broken because, in the rare case where the target function cares about the address of the reference (e.g., if it compares it to some known address), it would wind up with the address of a temporary copy, rather than the address of the original argument. Thus, the current component forwards references as references in all cases, including for basic types, except in the case of arrays and functions (that decay to pointers).
Comparison to bslmf_forwardingreftype:
The components bslmf_forwardingtype and bslmf_forwardingreftype serve the same purpose but have small behavioral differences. In general, we recommend bslmf_forwardingreftype (the new component) in most contexts.
Most notably, bslmf::ForwardingType (the older class) forwards fundamental and pointer types by value, where as bslmf::ForwardingRefType will forward fundamental and pointer types by const-reference. For example, bslmf::ForwardingType<int>Type is int where as bslmf::ForwardingRefType<int>Type is const int&. This applies to fundamental types, pointer types (including member-pointer types), and enum types (which we'll collectively call "basic types"). Forwarding these basic types by value was a performance optimization (and in some rare circumstances was hack needed by older compilers), which predated the standardization of many of the places where bslmf::ForwardingType was used (function and bind components in particular). The optimzation (potentially) being that passing an int by value is more likely to be done through a register, where as passing by reference is more likely to require de-referencing memory. Forwarding the types by const-reference, as the newer bslmf::ForwardingRefType does', is generally simpler and more in line with the modern C++ standard. Using bslmf::ForwardingRefType avoids some awkward edge cases at the expense of a possible optimization in parameter passing.
Usage:
In this section we show intended use of this component.
Example 1: Direct Look at Metafunction Results:
In this example, we invoke ForwardingType on a variety of types and look at the resulting Type member:
  struct MyType {};
  typedef MyType& MyTypeRef;

  void main()
      // Usage example.
  {
      typedef int                     T1;
      typedef int&                    T2;
      typedef const volatile double&  T3;
      typedef const double &          T4;
      typedef const float * &         T5;
      typedef const float * const &   T6;
      typedef MyType                  T7;
      typedef const MyType&           T8;
      typedef MyType&                 T9;
      typedef MyType                 *T10;
      typedef int                     T11[];
      typedef int                     T12[3];

      typedef int                     EXP1;
      typedef int&                    EXP2;
      typedef const volatile double&  EXP3;
      typedef const double &          EXP4;
      typedef const float * &         EXP5;
      typedef const float * const &   EXP6;
      typedef const MyType&           EXP7;
      typedef const MyType&           EXP8;
      typedef MyType&                 EXP9;
      typedef MyType                 *EXP10;
      typedef int                    *EXP11;
      typedef int                    *EXP12;

      assert((bsl::is_same<bslmf::ForwardingType<T1>::Type, EXP1>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T2>::Type, EXP2>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T3>::Type, EXP3>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T4>::Type, EXP4>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T5>::Type, EXP5>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T6>::Type, EXP6>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T7>::Type, EXP7>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T8>::Type, EXP8>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T9>::Type, EXP9>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T10>::Type, EXP10>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T11>::Type, EXP11>::value));
      assert((bsl::is_same<bslmf::ForwardingType<T12>::Type, EXP12>::value));
  }
Example 2: A Logging Invocation Wrapper:
This example illustrates the use of ForwardingType to efficiently implement a wrapper class that holds a function pointer and logs information about each call to the pointed-to-function through the wrapper. Suppose the pointed-to-function takes three arguments whose types are specified via template arguments, where the first argument is required to be convertible to int.
First we create a wrapper class that holds a function pointer of the desired type:
  // Primary template is never defined.
  template <class PROTOTYPE>
  class LoggingWrapper;

  template <class RET, class ARG1, class ARG2, class ARG3>
  class LoggingWrapper<RET(ARG1, ARG2, ARG3)> {
      // Specialization of wrapper for specified function prototype.

      RET (*d_function_p)(ARG1, ARG2, ARG3);

    public:
      explicit LoggingWrapper(RET (*function_p)(ARG1, ARG2, ARG3))
          // Create a 'LoggingWrapper' object for the specified 'function_p'
          // function.
        : d_function_p(function_p) { }
Then, we declare an overload of the function-call operator that actually invokes the wrapped function. In order to avoid excessive copies of pass-by-value arguments, we use ForwardingType to declare a more efficient intermediate argument type to forward to the wrapped function pointer:
      RET operator()(typename bslmf::ForwardingType<ARG1>::Type a1,
                     typename bslmf::ForwardingType<ARG2>::Type a2,
                     typename bslmf::ForwardingType<ARG3>::Type a3) const;
          // Invoke the stored function pointer with the specified 'a1',
          // 'a2', and 'a3' arguments, logging the invocation and returning
          // the result of the function pointer invocation.
  };
Next, we define logging functions that simply count the number of invocations and number of returns from invocations (e.g., to count how may invocations completed without exiting via exceptions):
  int invocations = 0, returns = 0;
  void logInvocation(int /* ignored */) { ++invocations; }
      // Log an invocation of the wrapped function.
  void logReturn(int /* ignored */) { ++returns; }
      // Log a return from the wrapped function.
Now, we implement operator() to call the logging functions, either side of calling the logged function through the wrapped pointer. To reconstitute the arguments to the function as close as possible to the types they were passed in as, we call the forwardToTarget member of ForwardingTypeUtil:
  template <class RET, class ARG1, class ARG2, class ARG3>
  RET LoggingWrapper<RET(ARG1, ARG2, ARG3)>::operator()(
                       typename bslmf::ForwardingType<ARG1>::Type a1,
                       typename bslmf::ForwardingType<ARG2>::Type a2,
                       typename bslmf::ForwardingType<ARG3>::Type a3) const {
      logInvocation(a1);
      RET r = d_function_p(
          bslmf::ForwardingTypeUtil<ARG1>::forwardToTarget(a1),
          bslmf::ForwardingTypeUtil<ARG2>::forwardToTarget(a2),
          bslmf::ForwardingTypeUtil<ARG3>::forwardToTarget(a3));
      logReturn(a1);
      return r;
  }
Then, in order to see this wrapper in action, we must define a function we wish to wrap. This function will take an argument of type ArgType that holds an integer value and keeps track of whether it has been directly constructed or copied from anther ArgType object. If it has been copied, it keeps track of how many "generations" of copy were made:
  class ArgType {
      int d_value;
      int d_copies;
    public:
      explicit ArgType(int v = 0) : d_value(v), d_copies(0) { }
          // Create an 'ArgType' object.  Optionally specify 'v' as the
          // initial value of this 'ArgType' object, otherwise this object
          // will hold the value 0.

      ArgType(const ArgType& original)
          // Create an 'ArgType' object that is a copy of the specified
          // 'original'.
      : d_value(original.d_value)
      , d_copies(original.d_copies + 1)
      { }

      int copies() const { return d_copies; }
          // Return the number of copies that this object is from the
          // original.

      int value() const { return d_value; }
          // Return the value of this object.
  };

  int myFunc(const short& i, ArgType& x, ArgType y)
      // Assign the specified 'x' the value of the specified 'y' and return
      // the 'value()' of 'x'.  Verify that the specified 'i' matches
      // 'y.copies()'.  'x' is passed by reference in order to demonstrate
      // forwarding of reference arguments.
  {
      assert(i == y.copies());
      x = y;
      return x.value();
  }
Finally, we create a instance of LoggingWrapper to wrap myFunc, and we invoke it. Note that y is copied into the second argument of operator() and is copied again when myFunc is invoked. However, it is not copied when operator() calls invoke() because the ForwardType of ArgType is const ArgType&, which does not create another copy. In C++11, if ArgType had a move constructor, then the number of copies would be only 1, since the final forwarding would be a move instead of a copy.
  void usageExample2()
      // Usage Example
  {
      ArgType x(0);
      ArgType y(99);

      LoggingWrapper<int(const short&, ArgType&, ArgType)> lw(myFunc);
      assert(0 == invocations && 0 == returns);
      lw(1, x, y);  // Expect exactly one copy of 'y'
      assert(1 == invocations && 1 == returns);
      assert(99 == x.value());
  }