3

What is the need for function pointers? The standard answer for this seems to be callbacks, but why can't we just pass a function?

The book I was reading on C++ demonstrates passing a function as a parameter, and acknowledges that in actual fact the compiled turns this into a function pointer and passes that instead, because functions are not actual objects. It showed the equivalent code using function pointers, which was slightly more complex - if the code is equivalent, why bother to use a function pointer.

I presume there is a case when is simply isn't possible to pass the function, and one must pass a pointer instead? Can someone give me an example of this case, as it would help me understand why function pointers are useful.

Consider the following code:

#include <iostream>

using namespace std;

int add(int x) {
        return ++x; //this is a copy, so it doesn't touch x
}

void runprint(int function(int x), int x) {
        cout << function(x) << endl;
}


int main() {
        runprint(add, 1);

        return 0;
}

We are passing a function as a parameter, not a pointer. The function accepting the function (!), is not accepting a pointer.

p0llard
  • 439
  • 6
  • 17
  • 2
    Why use pointers at all, instead of the things they point to? – juanchopanza May 20 '15 at 15:30
  • Can you show us the two codes? – Luchian Grigore May 20 '15 at 15:35
  • 1
    @juanchopanza I realise that is probably a rhetorical question, but why indeed? I read somewhere (can't remember where) that many C++ programmers feel they are over used, and they should only be used when they simply must be (eg. during dynamic memory allocation) - I don't see how they must be used in this case. – p0llard May 20 '15 at 15:37
  • @jdp407 Take a look for example at `quicksort` in C (of course in C++ you should use `std::sort` instead). It is using a function pointer as the comparator what makes it general, since it allows passing your custom comparator. How else would you write it? You cannot hardcode the comparator function into the library `quicksort`. I guess in C++ you can pass a function as a template. – vsoftco May 20 '15 at 15:40
  • @vsoftco I see where you are coming from, but say you have defined a function (your comparator), can you not simply pass the function name, as opposed to a pointer to the function? In this case it would not work, as std::sort expects a pointer, but why does it work this way? – p0llard May 20 '15 at 15:42
  • 1
    @jdp407 you pass indeed the function name (which decays to a function pointer), but the function that accepts it must define its parameter as a function pointer. – vsoftco May 20 '15 at 15:43
  • @jdp407 Function names only exist for the compiler and the linker. The processor has no idea what a function name is, only which address the current instruction is. This address is stored in a register that's often named "the instruction pointer". –  May 20 '15 at 15:44
  • 5
    To be clear, you can't pass a function. When you pass `my_func`, you actually pass the pointer. It's always a function pointer, everywhere. There are things like lambdas and std::function, but that's a completely different thing. – ElderBug May 20 '15 at 15:48
  • 2
    @jdp407 In the standard (I cannot find right now where exactly) it is specified that function parameters decay to function pointers, same as `void f(int arr[256])`, which is syntactic sugar for `void f(int* arr)` – vsoftco May 20 '15 at 15:49
  • @vsoftco In that case, why doesn't the function to which you are passing the pointer have to accept a pointer as an argument? If you look at the code I've posted, the function `runprint` is accepting a function as an argument, not a pointer. – p0llard May 20 '15 at 15:51
  • 1
    @jdp407: for your code the difference might be subtle - you're calling `runprint` and it's using the passed-in function straight away - but try to *store* the function in a variable so you can call it *at any arbitrary later time* and you'll see what pointers to functions are useful for. That said, as of C++11 there's a `std::function<>` type that hides and manages and generally cleans up use of function pointers for you. – Tony Delroy May 20 '15 at 15:52
  • @ElderBug is it reference to function or pointer to function? I seem to have mixed them up. – vsoftco May 20 '15 at 15:54
  • @vsoftco So if the 'non-pointer' form is translated to a pointer, why use the pointer form at all - is there some advantage? – p0llard May 20 '15 at 15:54
  • @jdp407 It's the same thing for pointers and references, many times you can use reference, sometimes you need pointers (for example, when it can be null). Google a bit about reference vs pointer usage, you might find what you want. – ElderBug May 20 '15 at 15:57
  • @TonyD So if I want to access the function outside the scope of the function it was passed to, I must use a function pointer? Can you give an example of when this might be required? Presumable it would require the function declaring – p0llard May 20 '15 at 15:59
  • @jdp407 Example: `struct MyObject { void setLogFn(void (*log_fn)(const char*)) { log_fn_ = log_fn; } void doStuff() { log_fn_("starting to do stuff..."); } void (*log_fn_)(const char*); };` - with this interface and usage, the code creating/using/calling `MyObject` can specify its own logging function to receive the `MyObject` logging strings, and discard or render them however it sees fit. – Tony Delroy May 20 '15 at 16:37

3 Answers3

3

function is considered a function pointer by g++:

$ cat fp.cpp
#include <typeinfo>
#include <iostream>

using namespace std;

int add(int x) {
        return ++x; //this is a copy, so it doesn't touch x
}

void runprint(int function(int x), int x) {
        cout << typeid(function).name() << endl;
        int (*f)(int);  // function pointer compatible with function argument
        f = function;
        cout << typeid(f).name() << endl;
        cout << function(x) << endl;
}


int main() {
        runprint(add, 1);

        return 0;
}
$ g++ -Wall -std=c++11 -o fp fp.cpp
$ ./fp
PFiiE
PFiiE
2
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • I'd add `cout << typeid(add).name() << endl;` and ``cout << typeid(&add).name() << endl;` for comparison, it shows that GCC does make a distinction between a function and a function pointer. The parameter to `runprint()` is a function pointer though, in spite of the missing pointer syntax. – Ulrich Eckhardt May 23 '15 at 19:50
  • @UlrichEckhardt Yes, I noticed that, too. I also noticed that there is a difference between &f, &&f etc, different to what I thought (until I thought a little harder). I had a hard time not only to understand the standard (that wouldn't be too surprising), but even to find relevant wording. (Probably because the mainstream case of just having a function name is not the only way to have something callable.) – Peter - Reinstate Monica May 24 '15 at 07:56
3

TL; DR

"Function" and "pointer to function" is the same.


There is the concept of a pointer, and the syntax of its usage; it's not clear what you are asking about.

Concept

A pointer to a function may be different from the function itself (the difference is not useful in c++ - see below) in that a function may occupy much space - its code can be arbitrarily complex. Manipulating (e.g. copying or searching/modifying) the code of a function is rarely useful, so c/c++ don't support it at all. If you want to modify the code of a function, cast a pointer to char*, applying all the necessary precautions (I have never done it).

So if you are writing C, all you need is pointers to functions.

However...

Syntax

If you have a pointer p to a function, how do you want to call the function?

(*p)(18); // call the function with parameter 18
p(18); // the same, but looks better!

There is the slightly cleaner syntax not involving the * sign. To support it, the authors of c/c++ invented the concept of "decay" - when your code mentions "a function", the compiler silently "corrects" it to mean "a pointer to a function" instead (in almost all circumstances; excuse me for not detailing further). This is very similar to the "decay" of an array to a pointer mentioned by vsoftco.

So in your example

void runprint(int function(int x), int x) {
        cout << function(x) << endl;
}

the "function" type is actually a "pointer to function" type. Indeed, if you try to "overload":

void runprint(int (*function)(int x), int x) {
        cout << function(x) << endl;
}

the compiler will complain about two identical functions with identical set of parameters.

Also, when making a variable of a function / pointer-to-function type

runprint(add, 1);

it also doesn't matter:

runprint(&add, 1); // does exactly the same

P.S. When declaring a function that receives a callback, I have mostly seen (and used) the explicitly written pointer. It has only now occurred to me that it's inconsistent to rely on function-to-pointer decay when calling the callback, but not when declaring my code. So if the question is

why does everyone declare callbacks using a pointer-to-function syntax, when a function syntax would be sufficient?

I'd answer "a matter of habit".

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • 3
    Functions are identical to pointers to functions in a similar way to arrays being identical to pointers. (They're related, but not identical.) – dyp May 20 '15 at 16:11
  • 1
    I don't remember right now any case in which they would be different. The array/pointer difference matters inside `sizeof`, but functions don't have `sizeof`. I guess the function and pointer to function *type* would be considered different in a `template`, but it's pretty far from the topic here... – anatolyg May 20 '15 at 16:13
  • Thank you very much - what I was missing was that the decay also occurred when defining the parameter, and so the parameter doesn't have to be an explicit function pointer. – p0llard May 20 '15 at 16:14
  • @anatolyg how about the "address-of" (`&`) operator? If you take `&some_func`, the type of the result will be a pointer-to-function, and **not** a pointer-to-pointer-to-function (which would be the case if functions and function pointers were identical, which they aren't.) – The Paramagnetic Croissant May 20 '15 at 16:14
  • In that case, if functions decay to function pointers, when would one ever use 'just a function pointer', if the two are syntactically equivalent? – p0llard May 20 '15 at 16:16
  • 1
    1) There can be references to functions. 2) The distinction of function types vs the types of pointers-to-functions are important e.g. in metaprogramming. 3) IIRC, C decays all functions before calling to pointers, whereas C++ implicitly dereferences pointers-to-funtions to get the function they're pointing to. 4) Member functions are significantly different to pointers to functions. 5) Pointers to functions can be null, functions themselves cannot be null. 6) Function names can refer to overload sets. – dyp May 20 '15 at 16:16
  • 1
    Functions and function pointers differ in that you can't take the `sizeof` a function but you may well take the size of a function pointer. Calling them equivalent or even equal seems fishy to me. The fact that you can call a function through a pointer without explicitly dereferencing it or that you get the address without explicitly requesting it makes this consistently fishy with the language. Oh well, such is life, let's move on. – Ulrich Eckhardt May 20 '15 at 16:18
  • @UlrichEckhardt the "let's move on" attitude is **entirely inappropriate** when educating a beginner about a language feature. Let's not use **lies** to introduce programmers to advanced parts of the language, just because "it's convenient" (or because the author of the answer doesn't know better). – The Paramagnetic Croissant May 20 '15 at 16:22
  • @TheParamagneticCroissant (and others) please see [this](http://stackoverflow.com/q/26559758/509868) – anatolyg May 20 '15 at 16:40
  • @anatolyg how is that question relevant? it's about that a seemingly function-type parameter **in a function parameter declaration list** is treated as an argument of type pointer-to-function. It has nothing to do with a function decaying to a function pointer when passed as a function parameter. – The Paramagnetic Croissant May 20 '15 at 16:43
  • 2
    @anatolyg "why does everyone declare callbacks using a pointer-to-function syntax, when a function syntax would be sufficient?" - that is indeed my question: I didn't realise that the two things were equivalent, and thought there was some case when one could only use pointer-to-function syntax. – p0llard May 20 '15 at 16:45
  • @TheParamagneticCroissant A function-type parameter **in a function parameter declaration list** is (also) what OP was talking about (IMHO). – anatolyg May 20 '15 at 16:46
  • @TheParamagneticCroissant: Re "If you take &some_func, the type of the result will be a pointer-to-function, and not a pointer-to-pointer-to-function (which would be the case if functions and function pointers were identical)" -- regarding the types you are right, but the reason is just the other way round. functions and function pointers are exchangeable to a larger extent than arrays and pointers to their first element: In `int arr[10]; int *ip = arr; int (*arrp)[10];`, **`ip` and `arrp` have different types, because `arr` and `&arr` have *different* types** (but the same value ;-) ). – Peter - Reinstate Monica May 20 '15 at 16:56
  • 1
    @PeterSchneider "functions and function pointers are exchangeable to a larger extent than arrays and pointers" – that's possible, but just as irrelevant. They are **still not identical,** that's all I'm saying. – The Paramagnetic Croissant May 20 '15 at 16:58
  • You know, @Paramagnetic, telling a *beginner* to accept and ignore certain things for now is a very pragmatic approach, in particular if the thing in question itself is inconsistent. Further, you seem to have implicitly called me a liar, was that your intention? If so, please say with which statement I did so. That said, the rest of your response shows to me that you didn't understand what I wanted to express. While you could blame my imprecise wording, I can still blame you for guessing in face of ambiguity. – Ulrich Eckhardt May 21 '15 at 05:50
  • @UlrichEckhardt it seems to me you are using the "it's pragmatic" argument as an excuse for explaining a feature incorrectly. I'm not convinced. "the rest of your response shows to me that you didn't understand what I wanted to express" – I did, I simply disagree with you. – The Paramagnetic Croissant May 21 '15 at 09:56
  • I did not excuse exlaining a feature incorrectly, @TheParamagneticCroissant. Question is whether you misrepresented what I said intentionally (then *you* are actually a liar) or that once more you didn't understand what I said (then you are just lying to yourself, which would still be a pity). – Ulrich Eckhardt May 21 '15 at 18:23
0

Function pointer is a pointer that holds the address of the function. Function pointer is to invoke the function even if the name of the function is unknown. This is practical in using different function at runtime.