Callback functions in C++
Asked Answered
W

12

438

In C++, when and how do you use a callback function?

EDIT:
I would like to see a simple example to write a callback function.

Waldner answered 19/2, 2010 at 17:16 Comment(1)
[This] (thispointer.com/…) explains basics about callback functions very well and easy to understand the concept.Achromat
M
685

Note: Most of the answers cover function pointers which is one possibility to achieve "callback" logic in C++, but as of today not the most favourable one I think.

What are callbacks(?) and why to use them(!)

A callback is a callable (see further down) accepted by a class or function, used to customize the current logic depending on that callback.

One reason to use callbacks is to write generic code which is independent of the logic in the called function and can be reused with different callbacks.

Many functions of the standard algorithms library <algorithm> use callbacks. For example, the for_each algorithm applies a unary callback to every item in a range of iterators:

template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
  for (; first != last; ++first) {
    f(*first);
  }
  return f;
}

which can be used to first increment and then print a vector by passing appropriate callables for example:

std::vector<double> v{ 1.0, 2.2, 4.0, 5.5, 7.2 };
double r = 4.0;
std::for_each(v.begin(), v.end(), [&](double & v) { v += r; });
std::for_each(v.begin(), v.end(), [](double v) { std::cout << v << " "; });

which prints

5 6.2 8 9.5 11.2

Another application of callbacks is the notification of callers of certain events which enables a certain amount of static / compile time flexibility.

Personally, I use a local optimization library that uses two different callbacks:

  • The first callback is called if a function value and the gradient based on a vector of input values are required (logic callback: function value determination/gradient derivation).
  • The second callback is called once for each algorithm step and receives certain information about the convergence of the algorithm (notification callback).

Thus, the library designer is not in charge of deciding what happens with the information that is given to the programmer via the notification callback and he needn't worry about how to actually determine function values because they're provided by the logic callback. Getting those things right is a task due to the library user and keeps the library slim and more generic.

Furthermore, callbacks can enable dynamic runtime behaviour.

Imagine some kind of game engine class which has a function that is fired, each time the user presses a button on his keyboard and a set of functions that control your game behaviour. With callbacks, you can (re)decide at runtime which action will be taken.

void player_jump();
void player_crouch();

class game_core
{
    std::array<void(*)(), total_num_keys> actions;
    // 
    void key_pressed(unsigned key_id)
    {
        if(actions[key_id])
            actions[key_id]();
    }
    
    // update keybind from the menu
    void update_keybind(unsigned key_id, void(*new_action)())
    {
        actions[key_id] = new_action;
    }
};

Here the function key_pressed uses the callbacks stored in actions to obtain the desired behaviour when a certain key is pressed. If the player chooses to change the button for jumping, the engine can call

game_core_instance.update_keybind(newly_selected_key, &player_jump);

and thus change the behaviour of a call to key_pressed (which calls player_jump) once this button is pressed the next time ingame.

What are callables in C++(11)?

See C++ concepts: Callable on cppreference for a more formal description.

Callback functionality can be realized in several ways in C++(11) since several different things turn out to be callable*:

  • Function pointers (including pointers to member functions)
  • std::function objects
  • Lambda expressions
  • Bind expressions
  • Function objects (classes with overloaded function call operator operator())

* Note: Pointer to data members are callable as well but no function is called at all.

Several important ways to write callbacks in detail

  • X.1 "Writing" a callback in this post means the syntax to declare and name the callback type.
  • X.2 "Calling" a callback refers to the syntax to call those objects.
  • X.3 "Using" a callback means the syntax when passing arguments to a function using a callback.

Note: As of C++17, a call like f(...) can be written as std::invoke(f, ...) which also handles the pointer to member case.

1. Function pointers

A function pointer is the 'simplest' (in terms of generality; in terms of readability arguably the worst) type a callback can have.

Let's have a simple function foo:

int foo (int x) { return 2+x; }

1.1 Writing a function pointer / type notation

A function pointer type has the notation

return_type (*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to foo has the type:
int (*)(int)

where a named function pointer type will look like

return_type (* name) (parameter_type_1, parameter_type_2, parameter_type_3)

// i.e. f_int_t is a type: function pointer taking one int argument, returning int
typedef int (*f_int_t) (int); 

// foo_p is a pointer to a function taking int returning int
// initialized by pointer to function foo taking int returning int
int (* foo_p)(int) = &foo; 
// can alternatively be written as 
f_int_t foo_p = &foo;

The using declaration gives us the option to make things a little bit more readable, since the typedef for f_int_t can also be written as:

using f_int_t = int(*)(int);

Where (at least for me) it is clearer that f_int_t is the new type alias and recognition of the function pointer type is also easier

And a declaration of a function using a callback of function pointer type will be:

// foobar having a callback argument named moo of type 
// pointer to function returning int taking int as its argument
int foobar (int x, int (*moo)(int));
// if f_int is the function pointer typedef from above we can also write foobar as:
int foobar (int x, f_int_t moo);

1.2 Callback call notation

The call notation follows the simple function call syntax:

int foobar (int x, int (*moo)(int))
{
    return x + moo(x); // function pointer moo called using argument x
}
// analog
int foobar (int x, f_int_t moo)
{
    return x + moo(x); // function pointer moo called using argument x
}

1.3 Callback use notation and compatible types

A callback function taking a function pointer can be called using function pointers.

Using a function that takes a function pointer callback is rather simple:

 int a = 5;
 int b = foobar(a, foo); // call foobar with pointer to foo as callback
 // can also be
 int b = foobar(a, &foo); // call foobar with pointer to foo as callback

1.4 Example

A function can be written that doesn't rely on how the callback works:

void tranform_every_int(int * v, unsigned n, int (*fp)(int))
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

where possible callbacks could be

int double_int(int x) { return 2*x; }
int square_int(int x) { return x*x; }

used like

int a[5] = {1, 2, 3, 4, 5};
tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};
tranform_every_int(&a[0], 5, square_int);
// now a == {4, 16, 36, 64, 100};

2. Pointer to a member function

A pointer to a member function (of some class C) is a special type of (and even more complex) function pointer which requires an object of type C to operate on.

struct C
{
    int y;
    int foo(int x) const { return x+y; }
};

2.1 Writing pointer to member function / type notation

A pointer to member function type for some class T has the notation

// can have more or less parameters
return_type (T::*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to C::foo has the type
int (C::*) (int)

where a named pointer to member function will -in analogy to the function pointer- look like this:

return_type (T::* name) (parameter_type_1, parameter_type_2, parameter_type_3)

// i.e. a type `f_C_int` representing a pointer to a member function of `C`
// taking int returning int is:
typedef int (C::* f_C_int_t) (int x); 

// The type of C_foo_p is a pointer to a member function of C taking int returning int
// Its value is initialized by a pointer to foo of C
int (C::* C_foo_p)(int) = &C::foo;
// which can also be written using the typedef:
f_C_int_t C_foo_p = &C::foo;

Example: Declaring a function taking a pointer to member function callback as one of its arguments:

// C_foobar having an argument named moo of type pointer to a member function of C
// where the callback returns int taking int as its argument
// also needs an object of type c
int C_foobar (int x, C const &c, int (C::*moo)(int));
// can equivalently be declared using the typedef above:
int C_foobar (int x, C const &c, f_C_int_t moo);

2.2 Callback call notation

The pointer to member function of C can be invoked, with respect to an object of type C by using member access operations on the dereferenced pointer. Note: Parenthesis required!

int C_foobar (int x, C const &c, int (C::*moo)(int))
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}
// analog
int C_foobar (int x, C const &c, f_C_int_t moo)
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}

Note: If a pointer to C is available the syntax is equivalent (where the pointer to C must be dereferenced as well):

int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + ((*c).*meow)(x); 
}
// or equivalent:
int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + (c->*meow)(x); 
}

2.3 Callback use notation and compatible types

A callback function taking a member function pointer of class T can be called using a member function pointer of class T.

Using a function that takes a pointer to a member function callback is -in analogy to function pointers- quite simple as well:

 C my_c{2}; // aggregate initialization
 int a = 5;
 int b = C_foobar(a, my_c, &C::foo); // call C_foobar with pointer to foo as its callback

3. std::function objects (header <functional>)

The std::function class is a polymorphic function wrapper to store, copy or invoke callables.

3.1 Writing a std::function object / type notation

The type of a std::function object storing a callable looks like:

std::function<return_type(parameter_type_1, parameter_type_2, parameter_type_3)>

// i.e. using the above function declaration of foo:
std::function<int(int)> stdf_foo = &foo;
// or C::foo:
std::function<int(const C&, int)> stdf_C_foo = &C::foo;

3.2 Callback call notation

The class std::function has operator() defined which can be used to invoke its target.

int stdf_foobar (int x, std::function<int(int)> moo)
{
    return x + moo(x); // std::function moo called
}
// or 
int stdf_C_foobar (int x, C const &c, std::function<int(C const &, int)> moo)
{
    return x + moo(c, x); // std::function moo called using c and x
}

3.3 Callback use notation and compatible types

The std::function callback is more generic than function pointers or pointer to member function since different types can be passed and implicitly converted into a std::function object.

3.3.1 Function pointers and pointers to member functions

A function pointer

int a = 2;
int b = stdf_foobar(a, &foo);
// b == 6 ( 2 + (2+2) )

or a pointer to member function

int a = 2;
C my_c{7}; // aggregate initialization
int b = stdf_C_foobar(a, c, &C::foo);
// b == 11 == ( 2 + (7+2) )

can be used.

3.3.2 Lambda expressions

An unnamed closure from a lambda expression can be stored in a std::function object:

int a = 2;
int c = 3;
int b = stdf_foobar(a, [c](int x) -> int { return 7+c*x; });
// b == 15 == a + (7 + c*a) == 2 + (7 + 3*2)

3.3.3 std::bind expressions

The result of a std::bind expression can be passed. For example by binding parameters to a function pointer call:

int foo_2 (int x, int y) { return 9*x + y; }
using std::placeholders::_1;

int a = 2;
int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
// b == 23 == 2 + ( 9*2 + 3 )
int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
// c == 49 == 2 + ( 9*5 + 2 )

Where also objects can be bound as the object for the invocation of pointer to member functions:

int a = 2;
C const my_c{7}; // aggregate initialization
int b = stdf_foobar(a, std::bind(&C::foo, my_c, _1));
// b == 1 == 2 + ( 2 + 7 )

3.3.4 Function objects

Objects of classes having a proper operator() overload can be stored inside a std::function object, as well.

struct Meow
{
  int y = 0;
  Meow(int y_) : y(y_) {}
  int operator()(int x) { return y * x; }
};
int a = 11;
int b = stdf_foobar(a, Meow{8});
// b == 99 == 11 + ( 8 * 11 )

3.4 Example

Changing the function pointer example to use std::function

void stdf_tranform_every_int(int * v, unsigned n, std::function<int(int)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

gives a whole lot more utility to that function because (see 3.3) we have more possibilities to use it:

// using function pointer still possible
int a[5] = {1, 2, 3, 4, 5};
stdf_tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};

// use it without having to write another function by using a lambda
stdf_tranform_every_int(&a[0], 5, [](int x) -> int { return x/2; });
// now a == {1, 2, 3, 4, 5}; again

// use std::bind :
int nine_x_and_y (int x, int y) { return 9*x + y; }
using std::placeholders::_1;
// calls nine_x_and_y for every int in a with y being 4 every time
stdf_tranform_every_int(&a[0], 5, std::bind(nine_x_and_y, _1, 4));
// now a == {13, 22, 31, 40, 49};

4. Templated callback type

Using templates, the code calling the callback can be even more general than using std::function objects.

Note that templates are a compile-time feature and are a design tool for compile-time polymorphism. If runtime dynamic behaviour is to be achieved through callbacks, templates will help but they won't induce runtime dynamics.

4.1 Writing (type notations) and calling templated callbacks

Generalizing i.e. the std_ftransform_every_int code from above even further can be achieved by using templates:

template<class R, class T>
void stdf_transform_every_int_templ(int * v,
  unsigned const n, std::function<R(T)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

with an even more general (as well as easiest) syntax for a callback type being a plain, to-be-deduced templated argument:

template<class F>
void transform_every_int_templ(int * v, 
  unsigned const n, F f)
{
  std::cout << "transform_every_int_templ<" 
    << type_name<F>() << ">\n";
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = f(v[i]);
  }
}

Note: The included output prints the type name deduced for templated type F. The implementation of type_name is given at the end of this post.

The most general implementation for the unary transformation of a range is part of the standard library, namely std::transform, which is also templated with respect to the iterated types.

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
  UnaryOperation unary_op)
{
  while (first1 != last1) {
    *d_first++ = unary_op(*first1++);
  }
  return d_first;
}

4.2 Examples using templated callbacks and compatible types

The compatible types for the templated std::function callback method stdf_transform_every_int_templ are identical to the above-mentioned types (see 3.4).

Using the templated version, however, the signature of the used callback may change a little:

// Let
int foo (int x) { return 2+x; }
int muh (int const &x) { return 3+x; }
int & woof (int &x) { x *= 4; return x; }

int a[5] = {1, 2, 3, 4, 5};
stdf_transform_every_int_templ<int,int>(&a[0], 5, &foo);
// a == {3, 4, 5, 6, 7}
stdf_transform_every_int_templ<int, int const &>(&a[0], 5, &muh);
// a == {6, 7, 8, 9, 10}
stdf_transform_every_int_templ<int, int &>(&a[0], 5, &woof);

Note: std_ftransform_every_int (non templated version; see above) does work with foo but not using muh.

// Let
void print_int(int * p, unsigned const n)
{
  bool f{ true };
  for (unsigned i = 0; i < n; ++i)
  {
    std::cout << (f ? "" : " ") << p[i]; 
    f = false;
  }
  std::cout << "\n";
}

The plain templated parameter of transform_every_int_templ can be every possible callable type.

int a[5] = { 1, 2, 3, 4, 5 };
print_int(a, 5);
transform_every_int_templ(&a[0], 5, foo);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, muh);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, woof);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, [](int x) -> int { return x + x + x; });
print_int(a, 5);
transform_every_int_templ(&a[0], 5, Meow{ 4 });
print_int(a, 5);
using std::placeholders::_1;
transform_every_int_templ(&a[0], 5, std::bind(foo_2, _1, 3));
print_int(a, 5);
transform_every_int_templ(&a[0], 5, std::function<int(int)>{&foo});
print_int(a, 5);

The above code prints:

1 2 3 4 5
transform_every_int_templ <int(*)(int)>
3 4 5 6 7
transform_every_int_templ <int(*)(int&)>
6 8 10 12 14
transform_every_int_templ <int& (*)(int&)>
9 11 13 15 17
transform_every_int_templ <main::{lambda(int)#1} >
27 33 39 45 51
transform_every_int_templ <Meow>
108 132 156 180 204
transform_every_int_templ <std::_Bind<int(*(std::_Placeholder<1>, int))(int, int)>>
975 1191 1407 1623 1839
transform_every_int_templ <std::function<int(int)>>
977 1193 1409 1625 1841

type_name implementation used above

#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cxxabi.h>

template <class T>
std::string type_name()
{
  typedef typename std::remove_reference<T>::type TR;
  std::unique_ptr<char, void(*)(void*)> own
    (abi::__cxa_demangle(typeid(TR).name(), nullptr,
    nullptr, nullptr), std::free);
  std::string r = own != nullptr?own.get():typeid(TR).name();
  if (std::is_const<TR>::value)
    r += " const";
  if (std::is_volatile<TR>::value)
    r += " volatile";
  if (std::is_lvalue_reference<T>::value)
    r += " &";
  else if (std::is_rvalue_reference<T>::value)
    r += " &&";
  return r;
}
        
Mackie answered 24/2, 2015 at 7:2 Comment(8)
@BogeyJammer: In case you haven't noticed: The answer has two parts. 1. A general explanation of "callbacks" with a small example. 2. A comprehensive list of different callables and the ways to write code using callbacks. You're welcome to not dig into detail or read the whole answer but just because you don't want a detailed view, it is not the case that the answer ineffective or "brutally copied". The topic is "c++ callbacks". Even if part 1 is ok for OP, others may find part 2 useful. Feel free to point out any lack of information or constructive criticism for the first part instead of -1.Mackie
@BogeyJammer I'm not new to programming but I am new to "modern c++". This answer gives me the exact context I need to reason about the role callbacks play in, specifically, c++. The OP may not have asked for multiple examples, but it is customary on SO, in a never-ending quest to educate a world of fools, to enumerate all possible solutions to a question. If it reads too much like a book the only advice I can offer is to practice a little bit by reading a few of them.Venereal
int b = foobar(a, foo); // call foobar with pointer to foo as callback, this is a typo right ? foo should be a pointer for this to work AFAIK.Rhizocarpous
@konoufo: [conv.func] of the C++11 standard says: "An lvalue of function type T can be converted to a prvalue of type “pointer to T.” The result is a pointer to the function." This is a standard conversion and as such happens implicitly. One could (of course) use the function pointer here.Mackie
one of the best answer I Read.Schreibe
It took me some (a very long, long) time to understand and figure this. Finally, this was the best explanation I found about this topic. An advice: play and try a bit with this on your C++ IDE. Finally you will get used to this absolutely awkward concepts and its naming conventions U_UVelvetvelveteen
Thanks for the amazing answer, despite what people are saying this answer has so much context and people searching for "callbacks in C++" lands here and stop thier quest for a while till they go on and try each of these in thier IDEs. Of course there is no excuse for people who like being spoonfed or looking for simple one liner answers. Thank you.Pearlinepearlman
Could anyone please elaborate on or redirect me to good resources for what's going on in the very first code block: template<class InputIt, class UnaryFunction> UnaryFunction for_each (...? especially the role of UnaryFunction. Thank you so much in advance!Balling
C
181

There is also the C way of doing callbacks: function pointers

// Define a type for the callback signature,
// it is not necessary but makes life easier

// Function pointer called CallbackType that takes a float
// and returns an int
typedef int (*CallbackType)(float);

void DoWork(CallbackType callback)
{
  float variable = 0.0f;
  
  // Do calculations
  
  // Call the callback with the variable, and retrieve the
  // result
  int result = callback(variable);

  // Do something with the result
}

int SomeCallback(float variable)
{
  int result;

  // Interpret variable

  return result;
}

int main(int argc, char ** argv)
{
  // Pass in SomeCallback to the DoWork
  DoWork(&SomeCallback);
}

Now, if you want to pass in class methods as callbacks, the declarations to those function pointers have more complex declarations, for example:

// Declaration:
typedef int (ClassName::*CallbackType)(float);

// This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
  // Class instance to invoke it through
  ClassName objectInstance;

  // Invocation
  int result = (objectInstance.*callback)(1.0f);
}

//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
  // Class pointer to invoke it through
  ClassName * pointerInstance;

  // Invocation
  int result = (pointerInstance->*callback)(1.0f);
}

int main(int argc, char ** argv)
{
  // Pass in SomeCallback to the DoWork
  DoWorkObject(&ClassName::Method);
  DoWorkPointer(&ClassName::Method);
}
Cacography answered 19/2, 2010 at 19:6 Comment(12)
There is an error in the class method example. The Invocation should be: (instance.*callback)(1.0f)Lantz
Thank you for pointing that out. I'll add both to illustrate invoking through an object, and through an object pointer.Cacography
This has the disadvantage from std::tr1:function in that the callback is typed per-class; this makes it impractical to use C-style callbacks when the object performing the call doesn't know the class of the object to be called.Concordance
How could I do it without typedefing the callback type? Is it even possible?Penguin
Yes you can. typedef is just syntactic sugar to make it more readable. Without typedef, the definition of DoWorkObject for function pointers would be: void DoWorkObject(int (*callback)(float)). For member pointers would be: void DoWorkObject(int (ClassName::*callback)(float))Cacography
Thank you! Simple and easy to understand! Not flexing like all the others.Zitvaa
For the future readers: typedef int (*CallbackType)(float); can also be written as using CallbackType = int(*)(float); which is much cleaner and easier to understand IMO!Balling
@Balling I just voted to reject your latest proposed edit, whose summary was “Previous editor had just removed the useful comment (and didn't even care to write proper summary. S/he just copy-pasted the summary!!)”. To explain what happened: I bet the edit you are trying to undo (by @Tarmo) comes from the review process for proposed edits; reviewers get the opportunity to “further edit” your proposal, and this actually shows up as a separate edit with the same summary (unfortunately).Halm
Now, to explain why I rejected your latest edit (hence agree with Tarmo’s alteration of your first edit): the original answer describes the “C way” but using is a C++ feature. On second thought, though, I’d have accepted that, provided that the comment was edited to state clearly that using was C++ rather than C.Halm
@Maëlan thank you for your time and for providing the clarification. I appreciate that. I just edited the answer with a note "In C++, ...". Hope that works otherwise I won't make any further edits for that particular thing. Thanks again!Balling
@Maëlan I think the edit process that you mentioned in your first comment seems a bit confusing, especially for not-so-experienced users like me. Because my edit had been approved and then @ Tarmo made further edits (basically an undo of some of my edits). IMO it would be great to have a feature that puts a note for edits made during the review process by the reviewers. Anyway, thanks for the explanation :)Balling
@Balling I agree. Feedback for the edit reviewing is a bit limited. (I cannot approve your edit myself, edits you get to review are drawn randomly, plus, I’ve exhausted my quota.)Halm
A
79

Scott Meyers gives a nice example:

class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
  typedef std::function<int (const GameCharacter&)> HealthCalcFunc;

  explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
  : healthFunc(hcf)
  { }

  int healthValue() const { return healthFunc(*this); }

private:
  HealthCalcFunc healthFunc;
};

I think the example says it all.

std::function<> is the "modern" way of writing C++ callbacks.

Arteritis answered 19/2, 2010 at 17:23 Comment(2)
Out of interest, which book does SM give this example in? Cheers :)Wheatley
I know this is old, but because I almost started doing this and it ended up not working on my setup (mingw), if you're using GCC version < 4.x, this method is not supported. Some of the dependencies I'm using won't compile without a lot of work in gcc version >= 4.0.1, so I'm stuck with using good old fashioned C-style callbacks, which work just fine.Lidalidah
P
38

A Callback function is a method that is passed into a routine, and called at some point by the routine to which it is passed.

This is very useful for making reusable software. For example, many operating system APIs (such as the Windows API) use callbacks heavily.

For example, if you wanted to work with files in a folder - you can call an API function, with your own routine, and your routine gets run once per file in the specified folder. This allows the API to be very flexible.

Picturesque answered 19/2, 2010 at 17:20 Comment(2)
This answer really doesn't average programmer tell anything he didn't know. I'm learning C++ while being familiar with many other languages. What callback is in general doesn't concern me.Penguin
The question is about how to use callbacks, not how to define them.Neilson
P
34

The accepted answer is very useful and quite comprehensive. However, the OP states

I would like to see a simple example to write a callback function.

So here you go, from C++11 you have std::function so there is no need for function pointers and similar stuff:

#include <functional>
#include <string>
#include <iostream>

void print_hashes(std::function<int (const std::string&)> hash_calculator) {
    std::string strings_to_hash[] = {"you", "saved", "my", "day"};
    for(auto s : strings_to_hash)
        std::cout << s << ":" << hash_calculator(s) << std::endl;    
}

int main() {
    print_hashes( [](const std::string& str) {   /** lambda expression */
        int result = 0;
        for (int i = 0; i < str.length(); i++)
            result += pow(31, i) * str.at(i);
        return result;
    });
    return 0;
}

This example is by the way somehow real, because you wish to call function print_hashes with different implementations of hash functions, for this purpose I provided a simple one. It receives a string, returns an int (a hash value of the provided string), and all that you need to remember from the syntax part is std::function<int (const std::string&)> which describes such function as an input argument of the function that will invoke it.

Peh answered 1/7, 2016 at 20:36 Comment(3)
out of all those answers above, this one made me understand what callbacks are and how to use them. thanks.Herrod
@MeharCharanSahai Glad to hear it :) You are welcome.Peh
This made me get the point at last, thanks. I think sometimes engineers should take them less seriously and understand that ultimate skill is in consciously simplifying what simple is not, IMO.Casandra
G
14

There isn't an explicit concept of a callback function in C++. Callback mechanisms are often implemented via function pointers, functor objects, or callback objects. The programmers have to explicitly design and implement callback functionality.

Edit based on feedback:

In spite of the negative feedback this answer has received, it is not wrong. I'll try to do a better job of explaining where I'm coming from.

C and C++ have everything you need to implement callback functions. The most common and trivial way to implement a callback function is to pass a function pointer as a function argument.

However, callback functions and function pointers are not synonymous. A function pointer is a language mechanism, while a callback function is a semantic concept. Function pointers are not the only way to implement a callback function - you can also use functors and even garden variety virtual functions. What makes a function call a callback is not the mechanism used to identify and call the function, but the context and semantics of the call. Saying something is a callback function implies a greater than normal separation between the calling function and the specific function being called, a looser conceptual coupling between the caller and the callee, with the caller having explicit control over what gets called. It is that fuzzy notion of looser conceptual coupling and caller-driven function selection that makes something a callback function, not the use of a function pointer.

For example, the .NET documentation for IFormatProvider says that "GetFormat is a callback method", even though it is just a run-of-the-mill interface method. I don't think anyone would argue that all virtual method calls are callback functions. What makes GetFormat a callback method is not the mechanics of how it is passed or invoked, but the semantics of the caller picking which object's GetFormat method will be called.

Some languages include features with explicit callback semantics, typically related to events and event handling. For example, C# has the event type with syntax and semantics explicitly designed around the concept of callbacks. Visual Basic has its Handles clause, which explicitly declares a method to be a callback function while abstracting away the concept of delegates or function pointers. In these cases, the semantic concept of a callback is integrated into the language itself.

C and C++, on the other hand, does not embed the semantic concept of callback functions nearly as explicitly. The mechanisms are there, the integrated semantics are not. You can implement callback functions just fine, but to get something more sophisticated which includes explicit callback semantics you have to build it on top of what C++ provides, such as what Qt did with their Signals and Slots.

In a nutshell, C++ has what you need to implement callbacks, often quite easily and trivially using function pointers. What it does not have is keywords and features whose semantics are specific to callbacks, such as raise, emit, Handles, event +=, etc. If you're coming from a language with those types of elements, the native callback support in C++ will feel neutered.

Grandpa answered 19/2, 2010 at 17:21 Comment(1)
fortunately this was not the first answer I read when I visited this page, otherwise I would have made ​​an immediate bounce!Amoreta
M
6

Callback functions are part of the C standard, an therefore also part of C++. But if you are working with C++, I would suggest you use the observer pattern instead: http://en.wikipedia.org/wiki/Observer_pattern

Morpho answered 14/12, 2010 at 16:45 Comment(4)
Callback functions are not necessarily synonymous with executing a function via a function pointer that was passed as an argument. By some definitions, the term callback function carries the additional semantics of notifying some other code of something that just happened, or that is time that something should happen. From that perspective, a callback function is not part of the C standard, but can be easily implemented using function pointers, which are part of the standard.Grandpa
"part of the C standard, an therefore also part of C++." This is a typical misunderstandang, but a misunderstanding nonetheless :-)Yager
I have to agree. I will leave it as it is, since it will only cause more confusion if I change it now. I meant to say that function pointer (!) are part of the standard. Saying anything different from that - I agree - is misleading.Morpho
In what way are callback functions "part of the C standard"? I don't think the fact that it supports functions and pointers to functions means that it specifically canonises callbacks as a language concept. Besides, as mentioned, that would not be directly relevant to C++ even if it was accurate. And it's especially not relevant when the OP asked "when and how" to use callbacks in C++ (a lame, too-broad question, but nonetheless), and your answer is a link-only admonition to do something different instead.Garibald
N
5

See the above definition where it states that a callback function is passed off to some other function and at some point it is called.

In C++ it is desirable to have callback functions call a classes method. When you do this you have access to the member data. If you use the C way of defining a callback you will have to point it to a static member function. This is not very desirable.

Here is how you can use callbacks in C++. Assume 4 files. A pair of .CPP/.H files for each class. Class C1 is the class with a method we want to callback. C2 calls back to C1's method. In this example the callback function takes 1 parameter which I added for the readers sake. The example doesn't show any objects being instantiated and used. One use case for this implementation is when you have one class that reads and stores data into temporary space and another that post processes the data. With a callback function, for every row of data read the callback can then process it. This technique cuts outs the overhead of the temporary space required. It is particularly useful for SQL queries that return a large amount of data which then has to be post-processed.

/////////////////////////////////////////////////////////////////////
// C1 H file

class C1
{
    public:
    C1() {};
    ~C1() {};
    void CALLBACK F1(int i);
};

/////////////////////////////////////////////////////////////////////
// C1 CPP file

void CALLBACK C1::F1(int i)
{
// Do stuff with C1, its methods and data, and even do stuff with the passed in parameter
}

/////////////////////////////////////////////////////////////////////
// C2 H File

class C1; // Forward declaration

class C2
{
    typedef void (CALLBACK C1::* pfnCallBack)(int i);
public:
    C2() {};
    ~C2() {};

    void Fn(C1 * pThat,pfnCallBack pFn);
};

/////////////////////////////////////////////////////////////////////
// C2 CPP File

void C2::Fn(C1 * pThat,pfnCallBack pFn)
{
    // Call a non-static method in C1
    int i = 1;
    (pThat->*pFn)(i);
}
Nerva answered 22/7, 2014 at 23:23 Comment(0)
F
1

The accepted answer is comprehensive but related to the question i just want to put an simple example here. I had a code that i'd written it a long time ago. i wanted to traverse a tree with in-order way (left-node then root-node then right-node) and whenever i reach to one Node i wanted to be able to call a arbitrary function so that it could do everything.

void inorder_traversal(Node *p, void *out, void (*callback)(Node *in, void *out))
{
    if (p == NULL)
        return;
    inorder_traversal(p->left, out, callback);
    callback(p, out); // call callback function like this.
    inorder_traversal(p->right, out, callback);
}


// Function like bellow can be used in callback of inorder_traversal.
void foo(Node *t, void *out = NULL)
{
    // You can just leave the out variable and working with specific node of tree. like bellow.
    // cout << t->item;
    // Or
    // You can assign value to out variable like below
    // Mention that the type of out is void * so that you must firstly cast it to your proper out.
    *((int *)out) += 1;
}
// This function use inorder_travesal function to count the number of nodes existing in the tree.
void number_nodes(Node *t)
{
    int sum = 0;
    inorder_traversal(t, &sum, foo);
    cout << sum;
}

 int main()
{

    Node *root = NULL;
    // What These functions perform is inserting an integer into a Tree data-structure.
    root = insert_tree(root, 6);
    root = insert_tree(root, 3);
    root = insert_tree(root, 8);
    root = insert_tree(root, 7);
    root = insert_tree(root, 9);
    root = insert_tree(root, 10);
    number_nodes(root);
}
Forelimb answered 8/6, 2019 at 13:55 Comment(2)
how does it answer the question?Brinkmanship
you know accepted answer is correct and comprehensive and i think there is no more thing to say generally. but i post an example of my usage of callback functions.Forelimb
C
0

Boost's signals2 allows you to subscribe generic member functions (without templates!) and in a threadsafe way.

Example: Document-View Signals can be used to implement flexible Document-View architectures. The document will contain a signal to which each of the views can connect. The following Document class defines a simple text document that supports mulitple views. Note that it stores a single signal to which all of the views will be connected.

class Document
{
public:
    typedef boost::signals2::signal<void ()>  signal_t;

public:
    Document()
    {}

    /* Connect a slot to the signal which will be emitted whenever
      text is appended to the document. */
    boost::signals2::connection connect(const signal_t::slot_type &subscriber)
    {
        return m_sig.connect(subscriber);
    }

    void append(const char* s)
    {
        m_text += s;
        m_sig();
    }

    const std::string& getText() const
    {
        return m_text;
    }

private:
    signal_t    m_sig;
    std::string m_text;
};

Next, we can begin to define views. The following TextView class provides a simple view of the document text.

class TextView
{
public:
    TextView(Document& doc): m_document(doc)
    {
        m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
    }

    ~TextView()
    {
        m_connection.disconnect();
    }

    void refresh() const
    {
        std::cout << "TextView: " << m_document.getText() << std::endl;
    }
private:
    Document&               m_document;
    boost::signals2::connection  m_connection;
};
Chinfest answered 15/8, 2016 at 19:50 Comment(0)
M
0

@Pixelchemist already gave a comprehensive answer. But as a web developmer, I can give some tips.

Usually we use tcp to develop a web framework, so usually we have a structure:

TcpServer listen port and register the socket to epoll or something
  -> TcpServer receive new connection 
    -> HttpConenction deal the data from the connection 
      -> HttpServer call Handler to deal with HttpConnection.
        -> Handler contain codes like save into database and fetch from db

We can develop the framework as the order, but it is not friendly to the user who only want to care the Handler. So it is time to use the callback.

Mutiple Handler written by user
  -> register the handler as callback property of HttpServer
    -> register the related methods in HttpServer to HttpConnection
      -> register the relate methods in HttpConnection to TcpServer

So user only need to register their handlers to httpserver(usually with some path string as key), the other thing is generic the framework can do.

So you can find we can treat the callback as a kind of context, that we want delegate to other to do for us. The core is that we don't know when is the best time to invoke the function, but the guy we delegate to know.

Mcneill answered 16/8, 2021 at 2:38 Comment(0)
N
0

After looking for a solution for embedded systems that:

  1. Cannot use std:: library (due to security, size, or heap alloc.)
  2. Cannot use boost:: library
  3. Required to use static allocations only.

But still want to implement things like std::bind or std::function for the callback implementation.

here are some links that can help:

Replacing std::bind by storing a lambda statically - this is useful if your callback parameters are stored in designated memory location. All the callbacks will be called without parameters, e.g., MyCallback() and so will be of the same type, as the parameters are 'stored' inside. As all callbacks 'look' the same they all can be easily placed in the same array with their opcodes. Link: std::function with static allocation in c++

Replacing std::function - c++ implementation to a 'function pointer' in C: (does not support bind/lambda storage, can use the bind from above): https://blog.coldflake.com/posts/C++-delegates-on-steroids/ (a bit restricted) or https://www.etlcpp.com/ (with more features)

Note: running on STM32

Norvil answered 12/3, 2023 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.