Did you know you can define functions like that in C?

Started by
11 comments, last by Krohm 10 years, 4 months ago

We all know the fearsome syntax for defining function pointers and function types, the one with the confusing parenthesis. You can find lot's of examples on the net. But here's a refresher.

For example, here's a type of a function taking an integer and returning an integer:

typedef int (foo_type)(int);
Type of a pointer to the same function:

typedef int (*foo_ptr_type)(int);

And here's a type of a another function that returns a pointer to the above function:


typedef foo_ptr_type (foo_getter_type)(char*);
Or this, also valid:

typedef foo_type* (foo_getter_type)(char*);

It is possible to unwrap above typedef, using a pretty obscure syntax(this is still pretty well known):


typedef int (*(foo_getter_type)(char*))(int);

Now can you guess how you would define a function that has the same type as int (*(foo_getter_type)(char*))(int)? No?

You just add brackets and give a name to char* argument: tongue.png


int (*(foo_getter)(char* name))(int)
{
  // I can't believe this thing compiled...
}

At first the function looks like it returns an int, but really it's returning a function that returns an int, go figure. I suspected this feature was there after I had a leftover "(int)" in my function declaration and compiler reported "cannot declare a function that returns a function". What? You can do that? So i started trying things out and discovered this syntax. I paged through K&R and couldn't find a mentioning that it's even possible, searched through net and could't find anything similar, so I guess this is one of the lesser known dusty corners of C that even language designers forgot they added it in there...

Advertisement
This is something second-year students at my alma mater are expected to learn in depth if they hope to actually pass their third semester. The parsing rules for declarations in C are rather simple once you get them down.

Note of course that this is all much easier with typedefs and is by far the recommended way for a C programmer to do something like this:

typedef int(*IF)(void);

extern IF make_function(void);

In C++11 this all gets even easier using the new using syntax as you no longer have to remember where the name goes inside the typedef:

using IF = int(*)();

IF make_function();

Sean Middleditch – Game Systems Engineer – Join my team!

Same here. They had us do a lot of absurd declarations no sane person will ever need, just for practice. "Declare a function that returns an array of function pointers and takes a pointer to an array of int". Looking back, I think all they really wanted to see was who would be smart enough to break it down into multiple typedefs and who would be insane enough to do it all in one line.

f@dzhttp://festini.device-zero.de

That's right, declarations I knew about as well, but this is definition. Anyway certainly not something I'd like to find in my code base, but that goes to my personal zoo of craziest C gibberish.

I've typedefed function pointers before, but never a function type. What can one do with the type of a function?

I've typedefed function pointers before, but never a function type. What can one do with the type of a function?

The are used all the time in C++. Many of the algorithmic functions take such a function. Functions that sort or search or generate or do something 'if' will all use them.

If you search through the C++ standard libraries you will find several hundred functions that take a predicate, binary predicate, unary function, binary function, or similar.

These are just the same as the prototypes above, typedefs for functions of the form bool(*UnaryPredicate)(T), bool(*BinaryPredicate)(T,T), void(*UnaryFunction)(T), void(*BinaryFunction)(T,T) or similar. The typedef looks very similar to the ones in the original post, except they are templates.

The benefit is that you can say: if(function(a,b)) and it just works. An example is the std::for_each implementation:


template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, UnaryFunction function)
{
   while (first!=last) {
      function (*first);
      ++first;
   }
   return fn; // or, since C++11: return move(fn);
}

In C++ just go through the <algorithm> header, you will find a large number of function overloads taking "Predicate" or "Function" signatures. You will find similar overloads in most of the container classes, and in many other locations through the libraries.

When people talk about using Lambda functions, this is precisely where a Lambda function is used.


These are just the same as the prototypes above, typedefs for functions of the form bool(*UnaryPredicate)(T), bool(*BinaryPredicate)(T,T), void(*UnaryFunction)(T), void(*BinaryFunction)(T,T) or similar. The typedef looks very similar to the ones in the original post, except they are templates.

Those are function pointer typedefs, not function typedefs.


These are just the same as the prototypes above, typedefs for functions of the form bool(*UnaryPredicate)(T), bool(*BinaryPredicate)(T,T), void(*UnaryFunction)(T), void(*BinaryFunction)(T,T) or similar. The typedef looks very similar to the ones in the original post, except they are templates.

Those are function pointer typedefs, not function typedefs.

you can make a pointer to a function type at least:

SomeFunctionType *foo;

not much sure of other uses.

would be cool if you could be like (in C):

SomeFunctionType Foo { ... }

or:

{

...

Foo(SomeFunctionType { ... }); // make and pass closure here...

...

}

or something.

but, alas...

actually, it is sort of possible to do closures in plain C (without compiler extensions, and using plain function pointers), but tends to involve some amount of pain (and some bit of trickery in the background, and some restrictions), making it not really particularly viable as a general-purpose development practice.


would be cool if you could be like (in C):
SomeFunctionType Foo { ... }

I think the reason why function types can't be used to define functions is because there would be no way to assign argument names, so they are pretty useless by themselves, you can't return them, you can't assign them, but you can construct other types with it.


What can one do with the type of a function?

Make std::function reasonable?

This topic is closed to new replies.

Advertisement