Chapter 6. Call Wrapper Basics

“The name of the song is called ‘Haddocks’ Eyes.’”

“Oh, that’s the name of the song, is it?” Alice said, trying to feel interested.

“No, you don’t understand,” the Knight said, looking a little vexed. “That’s what the name is called. The name really is ‘The Aged Aged Man.’”

“Then I ought to have said ‘That’s what the song is called’?” Alice corrected herself.

“No, you oughtn’t: that’s quite another thing! The song is called ‘Ways and Means’: but that’s only what it’s called, you know!”

“Well, what is the song, then?” said Alice, who was by this time completely bewildered.

“I was coming to that,” the Knight said. “The song really isA-sitting On A Gate’: and the tune’s my own invention.”

Through the Looking Glass
LEWIS CARROLL

Back in Chapter 3, we looked briefly at callable types, which are most commonly used to create objects with function call operators that can be passed to algorithms that need to make decisions involving the sequence element they’re currently looking at. That is, the function call operator is used just like a function: The algorithm calls it with the current element or a pair of elements, and the function call operator then returns a value that the algorithm uses. In fact, function pointers are callable types. However, it’s often better to use an object with a function call operator instead of a function pointer, because the compiler can generate inline code for the function call operator but usually can’t generate inline code for the function that a function pointer points at. Callable objects are also more flexible than function pointers, because different objects of the same type can hold different data values for use by the function call operator.[1]

TR1 introduces four new class templates that can be used to create function objects. These templates are defined in the header <functional>. Two of these class templates—reference_wrapper (discussed in Chapter 8) and function (discussed in Chapter 9)—can be used directly. The names and template argument types of the other two class templates are unspecified; objects of these types are created by calling the function templates mem_fn (discussed in Chapter 7) and bind (discussed in Chapter 10). In addition, the library provides two function templates—ref and cref—for creating objects whose types are specializations of the template reference_wrapper. Finally, the library provides a template named result_of (discussed in Section 6.4) that can be used to determine the return type of a call expression.

6.1. Terminology

Discussions of function objects are often confusing because the same words are used to mean different things. The documentation for the TR1 library introduces several terms of art for discussing and specifying what function objects do. First, a callable type is a pointer to function, a pointer to member function, a pointer to member data,[2] or a class type whose objects can appear to the left of a function call operator.[3]

For example, given a class C, the following are all callable types:

typedef float (*call0)(float);     // pointer to function
typedef long (C::*call1)();        // pointer to member function
typedef int C::*call2;             // pointer to member data

In addition, if the class C has a member operator() or a conversion to a pointer to function, the class is a callable type:

typedef C call3;     // function call operator

Naturally, a callable object is an object of a callable type. If we fill in the definition of the class C like this:

    class C
      {
    public:
      C(int i0) : i(i0) {}
      long get () const { return i; }
      int i;
      void operator ()(int ii) { i = ii; }
      };

we can define and initialize four callable objects like this:

    call0 c0 = cosf;             // pointer to  cosf in standard library
    call1 c1 = &C::get;          // pointer to C::get member function
    call2 c2 = &C::i;            // pointer to C::i data member
    call3 c3;                    // object of type C

A call wrapper type holds a callable object and supports a call operation that calls through that object; a call wrapper is an object of a call wrapper type. A target object is the callable object held by a call wrapper.

Example 6.1. Call Wrapper (funobjover/callwrap.cpp)


#include <iostream>
#include <math .h>
using std::cout;

class wrapper
  { // simple call wrapper type
  typedef float(*fp)(float);
public:
  wrapper(fp ptr) : fptr(ptr) {}
  float operator()(float arg)
    { // call operation; forwards to target object
    return fptr(arg);
    }
private:
  fp fptr;                        // target object
  };

int main()
  { // demonstrate use of call wrapper
  wrapper  wrap (cosf);           // call wrapper
  cout << "cosf (1.0) is " << cosf (1.0) << ' ';
  cout << "wrap (1.0) is " << wrap (1.0) << ' ';
  return 0;
  };


Every call wrapper can be copy constructed. In addition, if a call wrapper has an assignment operator, and if its copy constructor and assignment operator do not throw exceptions, it is a simple call wrapper. If it can be called with a list of arguments that are all lvalues, it is a forwarding call wrapper.[4]

6.2. Requirements for Call Wrapper Types

TR1 defines some additional terms that are used to describe requirements for callable types.

First, INVOKE(fn, t1, t2, …, tN) describes the effect of calling a callable object fn with the arguments t1, t2, , tN. Naturally, the effect depends on the type of the callable object. INVOKE is defined as follows:

1. (t1.*fn)(t2, …, tN) when fn is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T

2. ((*t1).*fn)(t2, …, tN) when fn is a pointer to a member function of a class T and t1 is not one of the types described in the previous item

3. t1.*fn when fn is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T

4. (*t1).*fn when fn is a pointer to member data of a class T and t1 is not one of the types described in the previous item

5. fn(t1, t2, …, tN) in all other cases

What this amounts to is that when the callable object is an ordinary function or a pointer to an ordinary function, INVOKE means to call that function, passing the rest of the arguments to the function call. When the callable object is a pointer to member, the next argument refers to the object that it should be applied to. That argument is the object itself, a reference to the object, a pointer to the object, or some kind of smart pointer that points to the object. The rest of the arguments are passed to the function call.

Second, INVOKE_R(fn, t1, t2, …, tN, Ret) describes the effect of calling a callable object fn with an explicit return type, Ret. It is defined as INVOKE(fn, t1, t2, …, tN) implicitly converted to Ret.[5]

Third, some call wrapper types have a weak result type; this means that they have a nested member named result_type that names a type determined from the call wrapper’s target type, Ty.

• If Ty is a function, reference to function, pointer to function, or pointer to member function, result_type is a synonym for the return type of Ty

• If Ty is a class type with a member type named result_type, result_type is a synonym for Ty::result_type

• Otherwise, result_type is not defined[6]

A few examples will help clarify what this rather dense text means:

struct base {
void f();
int g(double);
int h(double, double);
};
struct derived : base {
};

base b;
derived d;
base& br = d;

With these definitions, rule 1 gives the following meanings to these uses of INVOKE.

image

That is, the pointer to member function is called on the object or reference named by t1:

derived *dp = new derived;
base *bp = dp;
shared_ptr<base> sp(bp);

With these additional definitions, rule 2 gives the following meanings to these uses of ( INVOKE):

image

That is, the pointer to member function is called on the object that the argument t1 points to. Since it uniformly dereferences that argument, the rule works for any type whose operator* returns a reference to a suitable object. In particular, the rule works for shared_ptr objects.

Rules 3 and 4 give similar meanings to INVOKE uses that apply pointers to member data:

void func(base&);
struct fun_obj {
void operator()() const;
bool operator()(int) const;
};
fun_obj obj;

With these additional definitions, rule 5 gives the following meanings to these uses of INVOKE:

image

6.3. Header <functional> Synopsis

The TR1 library adds the following templates to the header <functional>:

namespace std {
  namespace tr1 {

    // CALLABLE OBJECT RETURN TYPES
template <class Ty> struct  result_of;

    // REFERENCE WRAPPERS
template <class Ty>
  struct reference_wrapper;
template <class Ty>
  reference_wrapper<const Ty> cref(const Ty&);
template <class Ty>
  reference_wrapper<const Ty> cref(reference_wrapper<Ty>);
template <class Ty>
  reference_wrapper<Ty> ref(Ty&);
template <class Ty>
  reference_wrapper<Ty> ref(reference_wrapper<Ty>);

    // FUNCTION OBJECT WRAPPERS
class bad_function_call;
template<class Fty>
  class function;
template<class Fty>
  bool operator==(const function<Fty>&, null_ptr_type npc);
template<class Fty>
  bool operator==(null_ptr_type npc, const function<Fty>&);
template<class Fty>
  bool operator!=(const function<Fty>&, null_ptr_type npc);
template<class Fty>
  bool operator!=(null_ptr_type npc, const function <Fty>&);
template<class Fty>
  void swap(function<Fty>& f1, function<Fty>& f2);

    // ENHANCED MEMBER POINTER ADAPTER
template <class Ret, class Ty>
  unspecified mem_fn(Ret Ty::*);

    // ENHANCED BINDERS
template <class Fty,
    class T1, class T2, …, class TN>
  unspecified bind(Fty, T1, T2, …, TN);
template <class Ret, class Fty,
    class T1, class T2, …, class TN>
  unspecified bind(Fty, T1, T2, …, TN);
template <class R, class Ty,
    class T1, class T2, …, class TN>
  unspecified bind(R Ty::*, T1, T2, …, TN);
template <class Ret, class R, class Ty,
    class T1, class T2, …, class TN>
  unspecified bind(R Ty::*, T1, T2, …, TN);

template <class Ty> struct is_placeholder;
template <class Ty> struct is_bind_expression;
  namespace placeholders {
    extern unspecified _1;
    } // placeholders

} };

Callable object return types are discussed in Section 6.4; reference wrappers in Chapter 8; function object wrappers in Chapter 9; the enhanced member pointer adapter in Chapter 7; and enhanced binders in Chapter 10.

6.4. The result_of Class Template

template<class Ty> struct result_of {
  typedef T1 type;
  };

One problem that you often run into when writing call wrapper types is that you need to figure out the return type of a call to the target object. The target object, as we’ve seen, can be a pointer to function, a pointer to a member function, a pointer to member data, or an object with at least one function call operator. The type of the value you get from a pointer to member data depends on the type of the object that it’s being applied to,[7] and the return type of an overloaded function call operator can depend on which overload is selected, which, in turn, depends on the arguments that are passed to it. Having to figure out the return type of one of these objects is tedious and error prone. TR1 provides a template, result_of, that gives you a uniform way of getting this information.

The template takes a single type argument that contains the type of a callable object and the list of argument types. This is done by using the syntax of a function type (discussed in Chapter 9). That is, the template argument consists of the callable type followed by a left parenthesis followed by a possibly empty list of argument types followed by a right parenthesis.[8] (As we’ll see later, a pointer to member is treated as a function whose first argument designates the object that the member function will be applied to, so the argument list for a pointer to member always has at least one argument type.) The resulting template specialization has a nested type named type that is a synonym for the return type of the template argument.

Example 6.2. Class Template result_of (funobjover/resultof.cpp)


#include <functional>
#include <math .h>
#include <iostream>
#include <typeinfo>
using std::tr1::result_of;
using std::ostream; using std::cout;

class C
  { // sample class
public:
  C(int i0) : i(i0) {}
  long get() const { return i; }
  int i;
  void operator()(int ii) { i = ii; }
  typedef void result_type;
  };

template <class Fty, class Arg>
void show_return(Fty fun, Arg arg)
  {   // show return type of fun(arg)
  typedef typename result_of<Fty(Arg)>::type ret;
  cout << "Return type of " << typeid(Fty).name()
    << " when called with " << typeid(Arg).name()
    << " is " << typeid(ret).name() << ' ';
  }

int main()
  { // demonstrate class template result_of
  C c(1);
  C *cp = &c;
  const C *ccp = &c;
  show_return(cosf, 1.0);                // cosf(float) returns float
  show_return(&C::get, cp);              // C::get() returns long
  show_return(&C::i, ccp);               // C::ihas type const int
  show_return(c, 3);                     // C() returns void
  return 0;
  }


Depending on which compiler you use, you may have to decipher the type names produced by this program; some compilers give rather cryptic names. In any event, the four calls to show_return should all display the correct return types for the four call wrapper types.

More formally, the nested type named by result_of<F(T1,T2,…,TN)>::type is the return type of the expression f(t1,t2,…,tN), where f is an object of type F, and t1, t2, , tN are objects of type T1, T2, , TN, respectively. When any of the types Ti is a reference, the corresponding object ti will be an lvalue; otherwise, ti will be an rvalue.

Most of the time when you need to use this template you’ll be inside the code of some other template, and the callable type will come in as a template argument. Occasionally, though, you may have to write out the declaration of the callable type and the argument list yourself. This can be confusing, because some callable types have their own argument type list, and you end up with two lists. If you have to write out the full argument list in a case like that, use a typedef for the callable type:[9]

typedef int (*func)(doubl);            // func is the callable type
result_of<func(float)>::type           // …

Unfortunately, result_of can’t be implemented in portable C++ code. It needs help from the compiler to figure out the return types of function call operators, because there is no way to examine the declaration of the function call operator to determine its return type.[10] Since it is not part of the C++ standard, TR1 ought to be implementable without that sort of help, so it has a list of rules that the implementation should follow if it can’t get the return type exactly right. Since TR1 permits this behavior, portable code that uses TR1 should not rely on getting the exact type but should assume that these rules will be applied.

For a callable type F and a set of argument types T1, T2, , TN, the type result_of<F(T1, T2, …, TN)>::type is determined as follows.

• If the type F is a function object defined in the standard library, the nested type type is a synonym for the return type of the call f(t1, t2, …, tN).

• If the type F is a pointer to function or a function type, the nested type type is a synonym for its return type.

If the type F is a pointer to member function, the nested type type is a synonym for its return type.

• If the type F is a pointer to data member of a class Ty, the nested type type is a synonym for cv R&, where R is the declared type of F, and cv represents the const and volatile qualifiers of the Ty object referred to by t1.

• If the type F is a class that has a member named result_type that names a type, the nested type type is a synonym for F::result_-type.[11]

• If the type F is a class that does not have a member named result_-type or that has a member named result_type that does not name a type:

— If the argument list is empty (N is 0) the nested type type is a synonym for void.

— Otherwise, the nested type type is a synonym for typename F::result<F(T1, T2, …, TN)>::type.[12]

• Otherwise, the program is ill-formed.

These rules are complicated because they try to accommodate existing practice. For the most part, you shouldn’t need this much detail. For ordinary functions and pointers to members, result_of gets the return type right. When you write a class with a function call operator, include a member typedef named result_type that names the return type of the function call operator. Don’t overload function call operators.

6.5. Interoperating with Existing Function Objects

The function object types in the standard C++ library take one or two arguments that are passed by value. The types of these arguments and the return type of a function object’s operator() are specified by template arguments that are passed to the template that defines the type of the function object. For example:

#include <functional>

std::plus<int> adder;
std::equal_to<int> comparator;

Here, the object adder has a function call operator that takes two arguments of type int and returns int. The template argument defines all three of those types. Similarly, the object comparator has a function call operator that takes two arguments of type int, specified by the template argument. The return type of the function call operator defined by the template std::equal_to, however, is always bool.

When you call the function call operators for either of these objects, the arguments you pass will be converted to the type that you gave as the template argument; in these two examples, that type is int:

adder(1.1,1.2);          // returns 2
comparator(1.1,1.2);     // returns true

In the TR1 library, the class template function works the same way: The types of the arguments to the function call operator are determined by the template argument used to instantiate function. The other three function object types, however, use a different scheme. Their function call operator is itself a template; its argument types are determined by the types that you call it with. And, as I mentioned earlier, its return type is often determined with the template result_of rather than an explicit template argument.

All the function object types in the standard C++ library describe their argument types and their return type with nested typedefs. Single-argument types have a nested type named argument_type, and two-argument types have two nested types: first_argument_type and second_argument_type. They all have a nested type named result_type. These types are defined by deriving from one of the templates unary_function and binary_function, as appropriate.[13]

These nested types are used by the standard C++ library call wrapper types to determine their own argument types and return types. For example, std::binder1st has a constructor that takes a function object and a value, as well as a function call operator that takes another value and returns the result of calling the function object with the two values. For example:

#include <functional>
void test()

// simple example of std::binder1st
typedef std::minus<int> int_minus;
int_minus sub;
std::binder1st<int_minus, int> two_minus(sub, 2);
two_minus(1);   // returns sub(2,1)
}

The implementation code for std::binder1st uses the nested typedefs in std::minus to determine the argument type and the return type for its function call operator. Here’s a simplified version of its declaration:[14]

template <class Op>
struct binder1st
     {   // interface to std::binder1st
     typedef typename
       Op::second_argument_type argument_type;
     typedef typename Op::result_type result_type;
     binder1st(const Op&,
                 const typename Op::first_argument_type&);
     result_type operator()(const argument_type&) const;
     };

Since the function call operator defined by binder1st takes one argument, the template defines the nested type argument_type. The value passed to the function call operator is, in turn, passed as the second argument in the call to the function call operator defined by Op, so binder1st defines argument_type as a synonym for Op::second_argument_type.

The TR1 library function object templates use a different scheme to determine their return type and argument types. As mentioned earlier, the return type and argument types for function are all included in the template argument. The rest of the templates define their function call operator as a template member function, so it gets its argument types from the arguments you pass when you call it. This means that, in general, it’s not possible to define nested type names for the argument types, because they aren’t known when the function object template is instantiated; it’s only at the point of the call that the argument types are known. Therefore, some of the usage patterns that work with function objects from the standard C++ library won’t work with function objects from the TR1 library.

Example 6.3. Incompatible (funobjover/fails.cpp)


#include <functional>
using std::not1; using std::bind1st;

using std::tr1::bind;
using namespace std::tr1::placeholders;
using std::binary_function;

struct do_something :
  binary_function<double, double, double>
  { // useless example of callable type with binary function call operator
  double operator()(double, double) const;
  };

void okay()
  { // C++03 call wrapper provides nested types
  not1(bind1st(do_something(), 1.0));
  }

void fails()
  { // TR1 call wrapper does not provide nested types
  not1(bind(do_something(), 1.0, _1));
  }


If you compile this code, the function okay will compile without problems. The function fails, as its name suggests, won’t compile. The function call bind(do_something(), 1.0, _1) is the TR1 equivalent of the bind1st call in the function okay. The second function fails because the type returned by the call to bind does not define the nested member argument_type, and the function not1 tries to use that nested name.

TR1 has a partial solution to making code that uses the TR1 library work with the existing standard C++ library function objects. The details depend on which of the TR1 function objects is being used. Generally speaking, when the target object takes one or two arguments and its type clearly defines the types of its arguments, the call wrapper defines the appropriate typedefs for its argument types. Often, the call wrapper can be called with more than one type related to its argument type,[15] but it can provide only one typedef for each of its argument types, so any additional types are simply not available to function objects from the standard C++ library. This is discussed in more detail for each of the TR1 library function objects.

Exercises

Exercise 1

For each of the following errors, write a simple test case containing the error, and try to compile it. In the error messages, look for the key words that relate to the error in the code.

1. Specializing result_of with a type argument having a different number of types in its argument list than the callable type actually takes.

2. Specializing result_of with a type argument whose callable type is a class having neither a nested type named result_type nor a nested template named result and that names at least one type in its argument list.

Exercise 2

For each of these uses of INVOKE, write the equivalent code, give its return type, and state whether the code is valid. Use the following declarations and definitions:

void func(int, double, double);
int func(int, double);

struct S
  {
  void mem_fun0(int) const;
  double mem_fun1(long, int) const;
  int mem_data;
  const int cmem_data;
  };
S s0;
const S s1;
const S *ptr = new S;
shared_ptr<S> sp(ptr);

struct oper
  {
  bool operator()() const;
  int operator()(int, double) const;
  };
oper op;

1. INVOKE(func, 1, 2, 3)

1. INVOKE(func, 1, 2)

2. INVOKE(func, 1, 2.0, double)

3. INVOKE(&S::mem_fun0, s0, 1)

4. INVOKE(&S::mem_fun0, s1, 1L, 2)

5. INVOKE(&S::mem_fun1, s1, 1L, 2)

6. INVOKE(&S::mem_fun1, ptr, 1L, 2)

7. INVOKE(&S::mem_fun0, sp, 1)

8. INVOKE(&S::mem_data, s0)

9. INVOKE(&S::cmem_data, s0)

10. INVOKE(&S::mem_data, s1)

11. INVOKE(&S::cmem_data, s1)

12. INVOKE(op)

13. INVOKE(op, 1, 1.0)

Exercise 3

Each part of this exercise has a type definition, an object of that type, and an expression that calls that object. For each of these expressions, what is the type of the result of that expression, what should result_of::type be for the types used in that expression under the compiler compromise rules, and what is the type of result_of::type using your implementation of the TR1 library? For example:

Type: typedef float(*t)(float);
Object: t obj = cosf;
Expression: obj(1.0)

The return type of obj(1.0) is float, the type of result_of::type is supposed to be float, and with the Dinkumware implementation of the TR1 library, the type of result_of::type is float. (Some of the exercises are a little more difficult than this.)

The exercises use the following definitions:

// types
struct S
{
void f();
int m;
double d;
};

struct S1
{
bool operator()() const;
};

struct S2
{
bool operator()() const;
typedef bool result_type;
};

struct S3
{
bool operator()() const;
int operator()(int) const;
};

struct S4
{
bool operator()() const;
int operator()(int) const;
typedef bool result_type;
};

// objects
S s;
const S cs;
S1 s1;
S2 s2;
S3 s3;
S4 s4;

image

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.213.87