far_mem_fun: A C++ Foreign Member Function Adapter

Abstract

Section 18.4.4.2 of The C++ Programming Language describes a technique "Member Function Adapters" for calling the same member function for each object in a container. The C++ STL provides several functions for this purpose. However, the STL does not provide for calling the member of another object outside of the container using the contained objects as parameters.

This document describes a technique for simplifying the use of foreign C++ object member functions as predicates.

Background

The purpose of these helper classes is to extend the functionality provided by The C++ Programming Language Section 18.4.4.2 Member Function Adapters.

The goal is to be able to have a class:

template <class Type> class basic_accumulator {
public:
  basic_accumulator() { m_typ = 0; };
  bool accumulate(Type i_typ) { m_typ += i_typ; };
  Type results(void) { return m_typ; };

protected:
  Type m_typ;
};
typedef basic_accumulator<int> accumulator;
Such that the following code has the expected results:
int main() {
  accumulator a;
  vector<int> v;

  v.push_back(1);
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(5);
  v.push_back(8);

  for_each(v.begin(),v.end(),far_mem_fun(a,&accumulator::accumulate));

  cout << a.results() << endl;

}
This should have the effect of calling a.accumulate() with each item contained by v passed as a single argument. IE, at runtime the for_each line above should be equivalent to:

a.accumulate(1);
a.accumulate(1);
a.accumulate(2);
a.accumulate(3);
a.accumulate(5);
a.accumulate(8);

The methodology described in 18.4.4.2 would be useful if the goal were to call the accumulate method of objects contained by v. Although that behavior is not desired, it is similar, and represents a useful starting point and suggestion of strategy.

18.4.4.2 uses a template class and a template function. This approach will follow the same mechanism, creating an interface point "far_mem_fun()" that works like "mem_fun()."

Note that a key difference is the idea of a function object that takes no parameters is meaningless in this context. Use in an iterative environment implies that an aspect of the call will be varied for each iteration. Whereas mem_fun is used with variance in the instance context of the member function call, far_mem_fun is used for variation in the argument, implying the existance of that argument. Thus, no implementation of a "far_mem_fun_t" class will be provided.

Implementation

The far_mem_fun function is very straightforward:

template <class Result, class Type, class Param>
far_mem_fun_t<Result,Type,Param> far_mem_fun(Type &ir_typ, Result (Type::*i_pmf)
(Param) ) {
  return far_mem_fun1_t<Result,Type,Param>ir_typ,i_pmf);
}
This template function, which requires no explicit specification when used, instantiates and returns an object of the far_mem_fun1_t template class, which is suitable for use as a predicate.

The most complicated part of this function is the construct:

Result (Type::*i_pmf)(Param)

This is not encountered in everyday C++. It is a use of the ::* pointer-to-member declarator, indicating that the variable i_pmf is a pointer to a member function of class Type that takes a single parameter of type Param and returns a result of type Result. This construct will be used several times in this implementation. The i_pmf value will be used in conjunction with a pointer to an object of type Type and the ->* operator to allow this implementation to reference arbitrary methods of arbitrary objects of arbitrary types. See The C++ Programming Language, Section 15.5 (Pointers to Members) for a more detailed discussion of ::* and ->*.

The purpose of the far_mem_fun1_t class is to contain a reference to the foreign object and its method during the iteration. Its implementation is also straightforward:

template <class Result, class Type, class Param> class far_mem_fun1_t
  : public std::unary_function<Param,Result> {
public:
  explicit far_mem_fun1_t(Type &ir_typ, Result (Type::*i_pmf)(Param)) {
    m_ptyp = &ir_typ;
    m_pmf = i_pmf;
  };
  Result operator()(Param i_prm) { return (m_ptyp->*(m_pmf)))(i_prm); };

protected:
  Type *m_ptyp;
  Result (Type::*m_pmf)(Param);
};

The constructor stores the foreign object and its method for later use.

The operator() method translates use of this functor as a predicate into a proper call to the method of the foreign object.

The implementation of const_far_mem_fun_t, far_mem_fun_ref_t, and const_far_mem_fun_ref_t template classes and their associated template functions for the sake of completeness is trivial. Furthermore, as an argument is guaranteed for this implementation, overloading of the () operator can be used to reduce the number of template class variations needed. This is left as an exercise for the reader.

Downloads

Source code for this article is in the public domain, for unlimited use and distribution.

References

Author

This document was written by Jeffrey D. Wheelhouse (jdw@wheelhouse.org). This is version 1.0, created 11/19/2002. Comments, suggestions, and corrections are welcomed via email. This document and its contents are placed into the public domain for unlimited use and distribution.

History

1.0
Initial release. 11/19/2002

Home Page | Software Projects | far_mem_fun