views:

176

answers:

4

That's an odd title. I would greatly appreciate it if somebody could clarify what exactly I'm asking because I'm not so sure myself.

I'm watching the Stanford videos on Programming Paradigms(that teacher is awesome) and I'm up to video five when he started doing this:

void *lSearch( void* key, void* base, int elemSize, int n, int (*cmpFn)(void*, void*))

Naturally, I thought to myself, "Oi, I didn't know you could declare a function and define it later!". So I created my own C++ test version.

int foo(int (*bar)(void*, void*));
int bar(void* a, void* b);

int main(int argc, char** argv)
{
    int *func = 0;
    foo(bar);

    cin.get();
    return 0;
}

int foo(int (*bar)(void*, void*))
{
    int c(10), d(15);
    int *a = &c;
    int *b = &d;
    bar(a, b);
    return 0;
}

int bar(void* a, void* b)
{
    cout << "Why hello there." << endl;
    return 0;
}

The question about the code is this: it fails if I declare function int *bar as a parameter of foo, but not int (*bar). Why!?

Also, the video confuses me in the fact that his lSearch definition

void* lSearch( /*params*/ , int (*cmpFn)(void*, void*)) is calling cmpFn in the definition, but when calling the lSearch function

lSearch( /*params*/, intCmp );

also calls the defined function int intCmp(void* elem1, void* elem2); and I don't get how that works. Why, in lSearch, is the function called cmpFn, but defined as intCmp, which is of type int, not int* and still works? And why does the function in lSearch not have to have defined parameters?

+10  A: 

int (*cmpFn)(void*, void*) is a function pointer -- a pointer to a function that can be called later. When you call iSearch you pass it a function that takes two void* and returns an int, and it binds that to the parameter cmpFn. Then iSearch can do something like int x = cmpFn(voidPtr1, voidPtr2); to call that function, passing it voidPtr1 and voidPtr2 as its arguments and storing the return value in x

You can try a simple example by just declaring a function pointer and using it in the same function:

int test1(int x) {return x;}
int test2(int x) {return x+1;}

int main(int argc, char** argv) {
    int (*fn)(int); // Function pointer named 'fn' that can hold a function that takes one int argument and returns an int
    int rtn;

    fn = test1; // Assign the 'test1' function to 'fn'
    rtn = fn(4); // Call 'fn' ('test1') with the argument 4; it returns 4 and stores it in 'rtn'

    fn = test2; // Assign the 'test2' function to 'fn'
    rtn = fn(4); // Call 'fn' ('test2') with the argument 4; it returns 5 and stores it in 'rtn'
}

It fails if you declare int *bar because that's not a function pointer, it's just a pointer to an integer. The syntax for function pointers is rtn_type (*name)(param1_type, param2_type, ...)

Michael Mrozek
+4  A: 

The question about the code is this: it fails if I declare function int *bar as a parameter of foo, but not int (*bar). Why!?

Because you would declare the function pointer as a pointer to a function returning int*. Notice that in parameter lists there is no difference between a function and a function pointer declaration, neither there is a difference in them between an array and a pointer declaration:

void f(void a());
void f(void(*a)());

void g(int a[]);
void g(int *a);

These are equivalent - the first two have their parameters have function pointer types, and the second two have their parameters have pointer type.

The star and the parentheses are written because it is consistent with its meaning outside of parameter lists where of course a function pointer and a function are different things.

Why, in lSearch, is the function called cmpFn, but defined as intCmp, which is of type int, not int* and still works?

The star signifies that it's a pointer to a function. The star doesn't attach to the return type (that's why the paren is there in the first place - to have the binding to the function instead of to the return type).

And why does the function in lSearch not have to have defined parameters?

The function pointer's parameter types are all what matters. You may give them names, but the names are ignored.

Johannes Schaub - litb
+2  A: 

There is a huge amount of tutorials devoted to function pointers.

The answer to your first question is the associativity of operator* it binds to the type first.

In the second example lSearch takes a function that takes to void* to an int. That is exactly how intCmp is declared.

Anyway: Function pointers aren't as important in C++. They should be wrapped in Functors (structs with operator() defined) and you should use templates instead of void*.

pmr
+1  A: 

The syntax:

int (*cmpFn)(void*, void*)

Means "a pointer to a function that takes two void* parameters and returns an int." The placement of the parentheses are simply part of the syntax. I suspect that they are necessary to help disambiguate in the instance where you have a function that returned an int* vs an int:

int* (*cmpFn)(void*, void*)

vs.

// Ambiguous - could be a pointer to a pointer to a function that returns int, or a pointer
// to a function that returns int*
int* *cmpFn(void*,void*) 

As for the calling conventions, think of cmpFn as a variable that holds a function. The name cmpFn is the name of that variable inside the function, but the function name itself could be different, just like with a regular variable:

void foo(int x) {
   // Variable is called "x" inside the function
}

void main() {
   int blah = 1;
   foo(blah);  // But it's called "blah" here
}

And with a function:

void myfunction(void *ptr) {
   // Does something.
}

void call_a_function( void (*func)(void*) ) {
   void* someptr;
   func(someptr); // Function is called "func" here.
}

void main() {
  call_a_function(myfunction); // But it's called "myfunction" here.
}
Eric Petroelje