how boost::function and boost::bind work
Asked Answered
B

1

85

I dislike having magic boxes scattered all over my code...how exactly do these two classes work to allow basically any function to be mapped to a function object even if the function<> has a completely different parameter set to the one im passing to boost::bind

It even works with different calling conventions (i.e. member methods are __thiscall under VC, but "normal" functions are generally __cdecl or __stdcall for those that need to be compatible with C.

Belgrade answered 9/2, 2009 at 8:28 Comment(4)
Dupe: #113238Tuberculosis
not really - this question is about bind and functionParget
Yes and thus that still leaves the question of how can bind map void MyClass:DoSomething(std::string str, int number) to boost::function<void(int)> via bind(&MyClass::DoSomething, instance, "Hello World", _1)Belgrade
20,000 visits holy cow this needs to be on the boost front page!Gaudreau
B
97

boost::function allows anything with an operator() with the right signature to be bound as the parameter, and the result of your bind can be called with a parameter int, so it can be bound to function<void(int)>.

This is how it works (this description applies alike for std::function):

boost::bind(&klass::member, instance, 0, _1) returns an object like this

struct unspecified_type
{
  ... some members ...
  return_type operator()(int i) const { return instance->*&klass::member(0, i);
}

where the return_type and int are inferred from the signature of klass::member, and the function pointer and bound parameter are in fact stored in the object, but that's not important

Now, boost::function doesn't do any type checking: It will take any object and any signature you provide in its template parameter, and create an object that's callable according to your signature and calls the object. If that's impossible, it's a compile error.

boost::function is actually an object like this:

template <class Sig>
class function
{
  function_impl<Sig>* f;
public:
  return_type operator()(argument_type arg0) const { return (*f)(arg0); }
};

where the return_type and argument_type are extracted from Sig, and f is dynamically allocated on the heap. That's needed to allow completely unrelated objects with different sizes bind to boost::function.

function_impl is just an abstract class

template <class Sig>
class function_impl
{
public:
  virtual return_type operator()(argument_type arg0) const=0;
};

The class that does all the work, is a concrete class derived from boost::function. There is one for each type of object you assign to boost::function

template <class Sig, class Object>
class function_impl_concrete : public function_impl<Sig>
{
  Object o
public:
  virtual return_type operator()(argument_type arg0) const=0 { return o(arg0); }
};

That means in your case, the assignment to boost function:

  1. instantiates a type function_impl_concrete<void(int), unspecified_type> (that's compile time, of course)
  2. creates a new object of that type on the heap
  3. assigns this object to the f member of boost::function

When you call the function object, it calls the virtual function of its implementation object, which will direct the call to your original function.

DISCLAIMER: Note that the names in this explanation are deliberately made up. Any resemblance to real persons or characters ... you know it. The purpose was to illustrate the principles.

Bowknot answered 9/2, 2009 at 12:40 Comment(5)
so is the contents of struct unspecified_type (ie the code in the operator() function) basicly generated from the arguments past to boost::bind on a case by case bases to allow any combination and number of arguments?Belgrade
No, there are templates of operator() that handle all arities (and different template arguments handle the combinations)Bowknot
In the last code block it reads: arg0) const=0 { return... ... I've never seen that before. I found one non-functioning example in a forum where a followup message linked to the C++ faq explaining that a pure virtual function could have a body, but I can't get any code to compile using syntax like that (clang & gcc).Thunderous
I should clarify my question slightly: A pure virtual can be given a body when declared with =0; with the body given later (eg, void Base::Blah() { /* ... */ }) ... I'm asking specifically about the notation used above; I'm guessing it's just a typo.Thunderous
@BrianVandenberg: I think it's standard conforming, although quite pointless.Consonant

© 2022 - 2024 — McMap. All rights reserved.