Sign in to follow this  
iMalc

[C++] Function References

Recommended Posts

It just recently came to my attention that there are probably a lot of C++ programmers out there that don't know about function references. People discuss function pointers from time to time, but almost never discuss function referencs. It's hard to find any info about them on the web too. I've actually been using them for a few years now. Example usage:
typedef int Func(int x);

int bar2(int x) { return x*2; }
int bar3(int x) { return x*3; }

void foo(Func &foobar)
{
    std::cout << foobar(5);
}

int main()
{
    foo(bar2);
    foo(bar3);
}
As you can see, they are actually cleaner to use than function pointers! Also, just like the difference between regular pointers and references, function references can't be made to point to a different function, and can't be NULL. Surely they'd thererfore allow for more optimisation possibilities? The only thing that isn't the same as regular references is that there's no usefulness to making them const. i.e.
void foo(const Func &foobar)
Such code causes the following in VS2008:
warning C4180: qualifier applied to function type has no meaning; ignored
Does anyone have any thoughts as to why they might not be very popular? Should we actually be preferring these over function pointers for the same reasons that we should prefer regular references over pointers? Do they work equally as well on all compilers?

Share this post


Link to post
Share on other sites
But these are function pointers. Just passed by reference, which is irrelevant in this particular case.

Or am I missing something.

The following also works:
template < class Func >
void foo(Func f) {
f(14);
}

...
foo(bar);

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
Does anyone have any thoughts as to why they might not be very popular?

I'd guess because most of the time both function pointers and function references are too low level and people grab a function object like boost::function instead.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
But these are function pointers. Just passed by reference, which is irrelevant in this particular case.

Or am I missing something.

The following also works:
template < class Func >
void foo(Func f) {
f(14);
}

...
foo(bar);


Not quite. If they were function pointers passed by reference, you could do this:


typedef void (&func_ref)();
typedef void (*func_ptr)();

void do_something(func_ref ref)
{
func_ptr ptr;
ref = ptr;
}



You can, however, do this:


void foo(func_ptr& ptr_ref)
{
func_ptr ptr;
ptr_ref = ptr;
}



so, they are a little different. Instead of being a function POINTER passed by reference it is the actual function passed by reference. To prove this compare typeid(void(&)()) to typeid(void()()) and they'll compare equal by operator==, yet both will compare different to typeid(void(*)());

I think in general function references aren't terribly useful for the same reason that function pointers aren't terribly useful. Most of the time if you want to take arbitrary function types you are either templating on the the function type, as Antheus did so that you can take anything that looks like a function, or if you need to be able to store it for later invocation, then using boost::function.

Share this post


Link to post
Share on other sites
Interesting info thanks.

I don't really know much about boost::function. I found this which describes the differences between boost::function and function pointers. Unfortunately not being one who's properly gotten into boost yet, it's hard to appreciate the value boost::function has.

The main place I've used function references is in a Sutherland-Hodgman clipping algorithm for performing clipping in 3D.
By having functions that take a point and return say the difference between the Z and X coordinates (for clipping against the right hand-side of the fustrum) or various other combinations, I implemented my clipper without using templates. I've seen other's use templates for the same thing but it just felt wrong instantiating 6 versions of the same function that can differ by about as much as a plus instead of a minus. I could have very well used function pointers instead of course.

Share this post


Link to post
Share on other sites
Let's have type X:
void foo(X x);
void foo(X &x);
void foo(X *x);


Are there any different rules for function pointers when passed via above forms than they are for all other types?

It seems to me that they should follow same rules. X is function, *X is function pointer, &X is reference to function - but they all deal with same type.

Quote:
void do_something(func_ref ref)
{
func_ptr ptr;
ref = ptr;
}

Shouldn't that be: 'ref = *ptr'?

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Let's have type X:
void foo(X x);
void foo(X &x);
void foo(X *x);


Are there any different rules for function pointers when passed via above forms than they are for all other types?

It seems to me that they should follow same rules. X is function, *X is function pointer, &X is reference to function - but they all deal with same type.

Quote:
void do_something(func_ref ref)
{
func_ptr ptr;
ref = ptr;
}

Shouldn't that be: 'ref = *ptr'?


Well that was kind of my point, that they aren't compatible. I was just responding to your comment that function references are actually function pointers, just passed by reference. If that were true, then the above should compile, because "reference to function pointer" and "function pointer" should be compatible.

Functions (and pointers and references) kind of break standard rules anyway though, which is part of why they're confusing, since for example you can do things like:


func_ptr ptr = &foo;
func_ptr ptr = foo;



and they mean the same thing.

Share this post


Link to post
Share on other sites
Quote:
Original post by cache_hit
You can, however, do this:

*** Source Snippet Removed ***

so, they are a little different. Instead of being a function POINTER passed by reference it is the actual function passed by reference.


Huh? Your code passes 'func_ptr&', and func_ptr is a function pointer type. How is that not "a function pointer passed by reference"?

Share this post


Link to post
Share on other sites
That obviously *is* a reference to a function pointer. However, i was responding to the claim that "a function reference is a function pointer, passed by reference.". If that were true, then func_ref should be the same as func_ptr&, unless im missing something (which is certainly possible given my spaciness the past few days). Thats what the example was intended to illustrate

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
function references can't be made to point to a different function, and can't be NULL.

Does anyone have any thoughts as to why they might not be very popular?

I believe you answered your own question.

Those are among the most useful features of function pointers. You can allow the function users to pass in NULL, with the meaning "don't call any function". You can allow the function users to set the function for a time, and replace it with a new one as they deem it appropriate.

Your optimization question is interesting. I would think the optimizer would see no difference between this and a more traditional function pointer. In both cases it is nothing more than accepting an address as a parameter, and then pushing parameters and jumping to the address. The optimizers would recognize both as function pointers, so it would optimize it the same.

Share this post


Link to post
Share on other sites
As far as I can tell there aren't any equivalent member function or member data references, just pointers, which can be argued makes using function references a tad inelegant.

On a vaguely related topic... Is there a consensus as to whether declaring functions implementing a specific interface through typedefs is a good idea or not? I didn't realize such a thing was allowed until recently and I can't quite make up my mind whether or not doing so makes life easier.

Share this post


Link to post
Share on other sites
It's generally considered a good idea, if only because function-pointer syntax is so tricky. Consider for example: if X is a typedef, 'const X&' is clear and means what you expect it to; whereas if you replace X with the type it stands for, the & might have to go somewhere else.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
It's generally considered a good idea, if only because function-pointer syntax is so tricky. Consider for example: if X is a typedef, 'const X&' is clear and means what you expect it to; whereas if you replace X with the type it stands for, the & might have to go somewhere else.
Agreed.

I meant something slightly different however, though I could have been a lot clearer. That is the idea of declaring regular functions implementing some common interface via typedefs. Functions which would usually, but not necessarily, be referred to through function pointers at some point.

As an example I ended up a couple of modules very much like this for a (toy) compiler:
// arithmetic.h
typedef int binary_t(int lhs, int rhs);

binary_t add;
binary_t sub;
binary_t mul;
binary_t div;

.
.
.

// arithmetic.c
int add(int lhs, int rhs} { return lhs + rhs }
int sub(int lhs, int rhs} { return lhs - rhs }
int mul(int lhs, int rhs} { return lhs * rhs }
int div(int lhs, int rhs} { return lhs / rhs }
In reality I had more like fifty of these, with several similar-looking set for separate tasks, and more often than not with enough parameters force line wrapping, but you get the idea.

Share this post


Link to post
Share on other sites
Whoa. I had no idea you could do that. That might serve as an argument against it; they look like global variable declarations, and I'm sure I'm not the only one who would be confused by that.

A one or two line comment at the top either saying, "declare a bunch of functions that do X", or preferably that explains about the function typedef thing, would help to clear things up. But of course if the only people that work on the codebase know how it works, the whole argument is basically moot.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this