std::bind
¶
std::bind
: Why?¶
Why? What’s the problem?
Answer:
Hard to explain
Best to see the problem first
Let’s start small, by simple example
Problem: we have …
Two dimensional points
(x,y)
A function to compute the distance between two points
We want:
A function to compute the distance from origin
(0,0)
What We Have¶
struct Point
{
Point(double x, double y)
: x(x), y(y) {}
double x, y;
};
double distance(Point p, Point q)
{
return std::sqrt(
std::pow(std::abs(p.x-q.x), 2) +
std::pow(std::abs(p.y-q.y), 2)
);
}
Retro C/C++¶
We have all that is needed
Could easily define a small function
⟶ Problem solved
But this would be soo retro!
double distance_origin(Point p)
{
return distance(p, {0,0});
}
The Real Problem¶
Nothing is wrong with small functions
Compiler will inline them
… and optimize away entirely
Defined centrally (public header file?) for further reuse
But…
What if they serve only one purpose?
Sample Problem
Compute the origin-distances of an array of points, and store
those in an equally sized array of double
!
Straightforward Implementation¶
Near the top of the implementation file …
static double distance_origin(Point p) {
return distance(p, {0,0});
}
And far down below, in the implementation section …
double distances_origin[sizeof(swarm)/sizeof(Point)];
std::transform(swarm, swarm+sizeof(swarm)/sizeof(Point),
distances_origin,
distance_origin);
More Sample Problems¶
Another Sample Problem
Compute the distances of an array of points from a given point, and
store those in an equally sized array of double
!
Possible solutions: as many as there are different tastes around …
Lets write another stupid function, basically a copy of
distance_origin
- only with(1,1)
instead of(0,0)
Even better: lets generalize! Functors! Function call operator!
More Straightforward Implementations¶
struct distance_point {
distance_point(Point origin) : origin(origin) {}
double operator()(Point p) const {
return distance(p, origin);
}
Point origin;
};
double distances_origin[sizeof(swarm)/sizeof(Point)];
std::transform(swarm, swarm+sizeof(swarm)/sizeof(Point),
distances_point,
distance_point({1,1}));
Readability¶
Provided that the helper code is only used once …
Readability is inversely proportional to amount of code
Number of bugs is directly proportional to amount of code
Helper implementation is nowhere near location of use
static
is the only keyword that enhances readability
Similar problem with many data structures and algorithms …
Sorting criteria:
std::sort
,std::map
, …Predicates:
std::find_if
,std::equal
, …Arbitrary adaptations where helper functions are needed
Most prominent (although relatively useless nowadays):
std::for_each
Introducing std::bind
(1)¶
Best done by example …
void f(int a, int b)
{
std::cout << a << ',' << b << std::endl;
}
f(1, 2);
|
1,2
|
What if we need the functionality of f(a, b)
, but are required
to pass a callable that takes no parameters?
Introducing std::bind
(2)¶
In other words, we need to create a function-like object that
wraps f(a,b)
that always calls f
with, say, a=1
and
b=2
.
Hardcoded parameters auto bound = std::bind(f, 1, 2);
bound();
|
1,2
|
Alternative: manually write function adaptor (functor) that remembers parameters until called
Origin: Boost (www.boost.org)
Introducing std::bind
(3)¶
Routing parameters into arbitrary positions: std::placeholders
Hardcoding only second parameter auto bound = std::bind(f, 42, std::placeholders::_1);
bound(7);
|
42,7
|
Exchanging parameters auto bound = std::bind(f,
std::placeholders::_2,
std::placeholders::_1);
bound(1,2);
|
2,1
|
Applying std::bind
(1)¶
So how does this apply to our ``std::transform`` problem?
Readability: we want to eliminate those annoying extra helper functions
Want to wrap existing
double distance(Point, Point)
which is similar in purpose but does not fit exactly
What we have …
struct Point {...};
double distance(Point, Point);
What we want …
std::transform(swarm, swarm+sizeof(swarm)/sizeof(Point),
distances_point,
SOMETHING WHICH TAKES ONE POINT);
Applying std::bind
(2)¶
std::transform(swarm, swarm+sizeof(swarm)/sizeof(Point),
distances_origin,
std::bind(distance, Point{0,0}, std::placeholders::_1));
// this is exactly the same as above
Summary
Readability: what remains unreadable is only the language itself
Have to get used to
std::bind
std::bind
vs. Lambda¶
Lambdas are usually a better alternative …
std::transform(swarm, swarm+sizeof(swarm)/sizeof(Point),
distances_origin,
[](Point p) { return distance({0,0}, p); });
A more advanced exercise
Use std::sort
to sort an array of points by their distance to a
given point.
A Bigger Picture: Types¶
What about types?
Goal is to have no runtime overhead
⟶ Late binding (polymorphism) ruled out
⟶ No common base class
Only the call signatures (parameter and return types) are the same
What does this mean?
Perfect for
<algorithm>
which is also designed for speedHave to be careful when code size is important
Client code has to be instantiated with the type
Tradeoff: speed, code size, elegance, design, taste …