In einem vorherigen Post wurden callbacks mit Hilfe von verzögerten Funktionionsaufrufen vorgestellt. Reallisiert wurden diese mit Funktionsobjekten (boost::function), die mit boost::bind gebaut wurden.

Mit Hilfe von std::for_each und boost::bind kann man Methoden von nicht nur einer, sondern von mehreren Instanzen einer Klasse in STL-Collections aufrufen. Der Algorithmus std::for_each erwartet als drittes Argument eine Referenz auf einen Funktor.  In der Iteration über alle Elemente der Collection übergibt er dem Funktor eine Referenz auf das aktuelle Element. boost::bind baut uns diesen Funktor. Dieser Funktor wiederrum ruft dann für jede Instanz in der Collection die übergebene Methode mit den übergebenen Parametern in der angegebenen Reihenfolge auf. Die Instanzen können in der Collection abgelegt sein als Kopie, Pointer oder Shared Pointer.

Beispielklasse A

Betrachten wir dazu eine Beispielklasse A mit 3 Methoden, die je eine unterschiedliche Signatur haben:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace classes
{
  class A
  {
  public:
    void f_1()
    {
      boost::format f(
        "%1% called. instance-addr: 0x%2%");
      f % __FUNCTION__ % this;
      std::cout << f.str() << std::endl;
    }
    int  f_2(double d, const std::string& s)
    {
      const int result = 1;
      boost::format f(
        "%1% called. instance-addr: 0x%2%. d: %3%, s: %4%. return: %5%");
      f % __FUNCTION__ % this % d % s % result;
      std::cout << f.str() << std::endl;
      return result;
    }
    void f_3(int a1, int a2, int a3)
    {
      boost::format f(
        "%1% called. instance-addr: 0x%2%. a1: %3%, a2: %4%, a3: %5%.");
      f % __FUNCTION__ % this % a1 % a2 % a3;
      std::cout << f.str() << std::endl;
    }
  };
}

Für Kopien

Hier ein Beispiel für Kopien von Instanzen in Containern. Aufgerufen wird die Methode A::f_1:

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Instances of classes::A for common use in case of objects and raw ptrs to
// objects:
classes::A a, b, c;
 
// Put object-copies in vector. Use boost::bind to build functor
// std::for_each can use. Provide functor with the required args in the
// right order for being able to call the indicated member function of the
// objects properly:
 
// Create vector. Create instances of A. Put copies of em in vector:
std::vector<classes::A> vec_copies;
vec_copies.push_back(a);
vec_copies.push_back(b);
vec_copies.push_back(c);
 
// Call f_1(): No return, no params
std::for_each(
  vec_copies.begin(),
  vec_copies.end(),
  boost::bind( // Builds functor of type '<void (classes::A&)>' ...
    &classes::A::f_1, // ... for call of type '<void (void)>'
    _1 // place holder for instance. Is each element of vec.
));

Für Pointer

Das ganze geht auch mit gleicher Syntax dank boost::bind für Pointer auf Instanzen.
Hier ein Beispiel für einen Methodenaufruf (A::f_2) mit Argumenten. Ein Rückgabewert der Methode wird von std::for_each nicht ausgewertet:

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
std::vector<classes::A*> vec_ptrs;
vec_ptrs.push_back(&a);
vec_ptrs.push_back(&b);
vec_ptrs.push_back(&c);
 
// Call f_2():
std::for_each(
  vec_ptrs.begin(),
  vec_ptrs.end(),
  boost::bind(
	&classes::A::f_2,
	_1, // place holder for instance. If used for member-functions,
	    // always the first param is the instance-address. Is each
	    // element of vec.
	0.42,			     // 1. (bound) arg
	"call of obj-ptrs with bind" // 2. (bound) arg
  ));

Für shared_ptr

Das ganze geht auch mit gleicher Syntax dank boost::bind für shared_ptr auf Instanzen.
Hier ein Beispiel für einen Methodenaufruf (A::f_2) mit Argumenten. Ein Rückgabewert der
Methode wird von std::for_each nicht ausgewertet:

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
std::vector<boost::shared_ptr<classes::A> > vec_shps;
vec_shps.push_back(boost::shared_ptr<classes::A>(new classes::A()));
vec_shps.push_back(boost::shared_ptr<classes::A>(new classes::A()));
vec_shps.push_back(boost::shared_ptr<classes::A>(new classes::A()));
 
// Call f_2():
std::for_each(
  vec_shps.begin(),
  vec_shps.end(),
  boost::bind(
  &classes::A::f_2,
  _1, // place holder for instance. If used for member-functions,
      // always the first param is the instance-address. Is each
      // element of vec.
  0.666,            // 1. (bound) arg
  "with shared_ptr!" // 2. (bound) arg
  ));

Direkter Aufruf mit BOOST_FOREACH

Etwas unsportlicher, weil leichter zu lesen ist der folgende direkte Aufruf
einer Methode (A::f_3) mehrerer Instanzen in einer STL-Collection mit BOOST_FOREACH:

88
89
90
91
92
boost::shared_ptr<classes::A> shp_a;
BOOST_FOREACH(shp_a,vec_shps)
{
  shp_a->f_3(1,2,3);
}

Selbstverständlich funktioniert dieser Aufruf ganz boost-like auch wieder mit Kopien und Raw-Pointern.

Für eine Map ist der Aufruf etwas anders, da hier Elemente vom Typ std::pair
gespeichert werden:

93
94
95
96
97
98
99
100
101
102
103
// direct walk with BOOST_FOR_EACH: map, shared_ptr to fn with 3 int args:
std::map<std::string,boost::shared_ptr<classes::A> > map_shps;
map_shps["0"] = vec_shps[0]; // see line 71 and following ...
map_shps["1"] = vec_shps[1];
map_shps["2"] = vec_shps[2];
 
std::pair<std::string,boost::shared_ptr<classes::A> > element;
BOOST_FOREACH(element,map_shps)
{
  element.second->f_3(1,2,3);
}

Ein vc++2008-projekt mit dem Beispielprogramm kann hier downgeloded werden.
Boost-Version: 1.42 aufwärts. Proj.C++.Additional Include Directories:
Pfad zu boost angeben, Proj.Linker.General.Additional Library Directories:
Pfad zu boost\stage\lib angeben.

play_with_boost_function_vc++2008_proj

Bis zum nächsten Post,

Daniel