Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Returning Function Pointers

42 views
Skip to first unread message

Doug Mika

unread,
May 1, 2015, 1:58:30 PM5/1/15
to
Well, I found something on function pointers, but id didn't cover how it looks when your function returns these. That is, until I found the following piece of code I attach below

#include <math.h>
#include <stdio.h>

enum
{
OPER_SIN,
OPER_COS,
OPER_TAN,
OPER_SQUARE
};

struct oper
{
int type;
double (*func)(double);
};

double square(double val)
{
return val * val;
}

struct oper oper_lut[] =
{
{OPER_SIN, sin},
{OPER_COS, cos},
{OPER_TAN, tan},
{OPER_SQUARE, square},

};

double (*get_oper(int oper_type))(double)
{
int n;
for (n=0;n<(sizeof(oper_lut)/sizeof(oper_lut[0]));n++)
{
if (oper_type == oper_lut[n].type)
{
return oper_lut[n].func;
}
}
}

void apply_operation(double *dat, int length, double (*oper)(double))
{
int k;
for (k=0;k<length;k++)
{
dat[k] = oper(dat[k]);
}
}

void print_vector(double *dat, int length)
{
int k;
for (k=0;k<length;k++)
{
printf("%g ", dat[k]);
}
printf("\n");
}

void call_it(void)
{
double vals[] = {1,2,3};
double (*func)(double);
func = get_oper(OPER_SIN);
apply_operation(vals, sizeof(vals)/sizeof(vals[0]), func);
print_vector(vals, sizeof(vals)/sizeof(vals[0]));
func = get_oper(OPER_COS);
apply_operation(vals, sizeof(vals)/sizeof(vals[0]), func);
print_vector(vals, sizeof(vals)/sizeof(vals[0]));
func = get_oper(OPER_TAN);
apply_operation(vals, sizeof(vals)/sizeof(vals[0]), func);
print_vector(vals, sizeof(vals)/sizeof(vals[0]));
func = get_oper(OPER_SQUARE);
apply_operation(vals, sizeof(vals)/sizeof(vals[0]), func);
print_vector(vals, sizeof(vals)/sizeof(vals[0]));
}

int main(int argc, char *argv[])
{
call_it();
}

I had two questions regarding this:
1) (I don't know why but this struct definition seems more complicated than necessary) What is this below, a struct or an array?:

struct oper oper_lut[] =
{
{OPER_SIN, sin},
{OPER_COS, cos},
{OPER_TAN, tan},
{OPER_SQUARE, square},

};

2) What is this:
double (*get_oper(int oper_type))(double)

Victor Bazarov

unread,
May 1, 2015, 3:24:41 PM5/1/15
to
You need to learn to read declarations. It will help a lot.

Start with the name (can you identify the name here? It's "oper_lut"),
then look to the *right*. If there is no bracket, you then proceed to
the *left*. If there is a bracket, you proceed to say "is an array", so
you get so far

oper_lut ...(to the right there is a bracket) ... is an array of
(how many elements? we don't know, the bracket closes before the size
is specified) ... unknown size ... of (continue to the right, there is
an equal sign, which means there is an initializer, which is not part of
the declaration, so we reverse the direction)... "struct oper".
Anything else? Nothing. So, what did we get?

'oper_lut' is an array of unknown size of 'struct oper'.

A better approach can be found on the web, I've seen it, and I am sure
you can find it. Look for "how to read C++ declarations"

> 2) What is this:
> double (*get_oper(int oper_type))(double)

Same approach. Can you find the name? is 'oper_type' the name of the
object being declared? Hint: no. What else can be a name? Hint: it's
actually 'get_oper'. So you look to its *right*, you don't see a
bracket, so (it's not an array) you reverse the direction. You see the
asterisk. It's _a_pointer_ ! To what? Keep going... Drat, a closing
(if going in that direction) parenthesis. Revers the direction. An
opening parenthesis, followed by a declaration. An argument list! So,
we say "a function that takes {all embedded declarations go here}" and
keep naming those until we find a closing parenthesis, which is very
close. What did we get so far?

" 'get_oper' (to the left)... is a pointer (reverse the direction) ...
to a function that takes one argument of type int"

Now, we need to know what that function with one int argument returns,
right? So we keep going in the same direction (which was to the right).
Drat, another opening parenthesis. What does a parenthesis say?
Depends on what's inside. And inside is a declaration (it's a bare
type), so we add "a function that takes". What follows? The argument
list -- "a double". Closing parenthesis means we keep going, but we see
it's the end of the declaration. So we reverse direction to say "and
returns"... What's left? The return type -- "a double".

That is, we get

"'get_oper' is a pointer to a function that takes one int and returns a
function that takes a double and returns a double."

That's not easy. You'll get the hang of it eventually, though.

Since a function cannot be returned, a function that returns a function
simply returns a _pointer_ to a function.

V
--
I do not respond to top-posted replies, please don't ask

Doug Mika

unread,
May 1, 2015, 4:04:49 PM5/1/15
to

> struct oper oper_lut[] =
> {
> {OPER_SIN, sin},
> {OPER_COS, cos},
> {OPER_TAN, tan},
> {OPER_SQUARE, square},
>
> };

Even with your explenation, since oper is defined as a struct shouldn't we write:
oper oper_lut[] =
{
{OPER_SIN, sin},
{OPER_COS, cos},
{OPER_TAN, tan},
{OPER_SQUARE, square},

};

this is what's so unusual, why is the keyword "struct" in front of the array declaration?


Victor Bazarov

unread,
May 1, 2015, 4:11:52 PM5/1/15
to
Nothing. It's a C-ism.

BTW, it's not in front of an array declaration. It's in front of 'oper'
token, which by itself a "type-id." Supplied with the 'struct' it
turnes into an "elaborate type specifier". It's perfectly fine without
it, but in C we can't use 'oper' as a type in a declaration, unless it's
declared using a typedef, and we have to use 'struct' in front of it to
designate that 'oper' should be looked up among some struct definitions
(or declarations, I don't remember what they are called in C).

In C++ we don't need to do it *except* to solve ambiguity. In this
particular case there is no ambiguity. The code was probably written by
a C programmer...

Ben Bacarisse

unread,
May 1, 2015, 6:51:18 PM5/1/15
to
Victor Bazarov <v.ba...@comcast.invalid> writes:
<snip>
> You need to learn to read declarations. It will help a lot.

Yes, but it's tricky...

<snip>
> A better approach can be found on the web, I've seen it, and I am sure
> you can find it. Look for "how to read C++ declarations"

I wrote one, but reading it over just now I'm not sure that it's any
better than the others: http://www.bsb.me.uk/c-types

> On 5/1/2015 1:58 PM, Doug Mika wrote:
<snip>
>> 2) What is this:
>> double (*get_oper(int oper_type))(double)

<snip discussion>

> That is, we get
>
> "'get_oper' is a pointer to a function that takes one int and returns
> a function that takes a double and returns a double."

No, it's a function taking an int and returning a pointer to a function
taking a double and returning a double.

<snip>
--
Ben.

Ben Bacarisse

unread,
May 1, 2015, 6:54:30 PM5/1/15
to
Victor Bazarov <v.ba...@comcast.invalid> writes:
<snip>
> In C++ we don't need to do it *except* to solve ambiguity. In this
> particular case there is no ambiguity. The code was probably written
> by a C programmer...

It probably was, but that sounds a little dismissive -- it may have been
written by a C programmer because it's C! (There's a fair bit of
evidence that the posted code is C not C++ though one can rarely be 100%
sure.)

--
Ben.

Ben Bacarisse

unread,
May 1, 2015, 7:00:33 PM5/1/15
to
Doug Mika <doug...@gmail.com> writes:
<snip>
> I had two questions regarding this:
> 1) (I don't know why but this struct definition seems more complicated
> than necessary) What is this below, a struct or an array?:

It's an array.

> struct oper oper_lut[] =
> {
> {OPER_SIN, sin},
> {OPER_COS, cos},
> {OPER_TAN, tan},
> {OPER_SQUARE, square},
>
> };

The code is (probably) written in C, so the element type is "struct
oper". All struct types start "struct ..." in C.

> 2) What is this:
> double (*get_oper(int oper_type))(double)

It's a function taking an int and returning a pointer to a function that
takes a double and returns a double.

It is generally clearer to write such types using type definitions for
the parts:

typedef double operation(double);
operation *get_oper(int oper_type);

(and you'd use that type in the struct as well).

--
Ben.

Jorgen Grahn

unread,
May 2, 2015, 4:17:59 AM5/2/15
to
It's allowed, I think, to be a bit annoyed by decent C code being
posted to a C++ group.

Speaking of that, it could be more interesting for the OP to rewrite
the example as idiomatic C++ code. Functors (lambdas?) stored in a
std::map maybe, std::for_each ... The things you learn by doing that
are more useful than function pointers.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Ben Bacarisse

unread,
May 2, 2015, 6:53:10 AM5/2/15
to
Jorgen Grahn <grahn...@snipabacken.se> writes:

> On Fri, 2015-05-01, Ben Bacarisse wrote:
>> Victor Bazarov <v.ba...@comcast.invalid> writes:
>> <snip>
>>> In C++ we don't need to do it *except* to solve ambiguity. In this
>>> particular case there is no ambiguity. The code was probably written
>>> by a C programmer...
>>
>> It probably was, but that sounds a little dismissive -- it may have been
>> written by a C programmer because it's C! (There's a fair bit of
>> evidence that the posted code is C not C++ though one can rarely be 100%
>> sure.)
>
> It's allowed, I think, to be a bit annoyed by decent C code being
> posted to a C++ group.

Oh, yes, sure, but that's not the fault of the programmer who wrote the
code that got posted!

By the way, it's not particularly good C code either -- get_oper fails
to return a value in some cases, and I don't know why anyone would
choose to write the declaration without a typedef, particularly since
the function type is needed in more than one place.

> Speaking of that, it could be more interesting for the OP to rewrite
> the example as idiomatic C++ code. Functors (lambdas?) stored in a
> std::map maybe, std::for_each ... The things you learn by doing that
> are more useful than function pointers.

Good idea. I just did that out of interest and I'll post mine if the OP
has a stab at it.

--
Ben.

Richard

unread,
May 2, 2015, 10:18:48 PM5/2/15
to
[Please do not mail me a copy of your followup]

Doug Mika <doug...@gmail.com> spake the secret code
<52b23a52-11df-4751...@googlegroups.com> thusly:

>Well, I found something on function pointers, but id didn't cover how it
>looks when your function returns these.
>That is, until I found the following piece of code I attach below

What you found in a piece of C code.

Please don't code C in C++.

Use C++ mechanisms and abstractions to achieve your purposes instead of
C mechanisms and its limited abstractions.

Function pointers are how C achieves variability of function, but C++
has better mechanisms: classes, virtual methods and templates to name
just a few.

Function pointer syntax gets ugly quite quickly, which is one of the
reasons that people often use them with typedefs in order to make things
sane again. If you just use the abstraction mechanisms in C++ instead,
you can return to normalcy.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Permil Garg

unread,
May 3, 2015, 12:25:13 PM5/3/15
to
> struct oper oper_lut[] =
> {
> {OPER_SIN, sin},
> {OPER_COS, cos},
> {OPER_TAN, tan},
> {OPER_SQUARE, square},
>
> };

This is a array of struct (oper) which named oper_lut[] and it is initialized with 4 values. In braces, the value of array to initialize. Before (,) is the enum value and after (,) is function name.
0 new messages