simplifying c code with function pointers

G

goldfita

I love this place. This one problem keeps coming back to bite me.
Suppose I have the following situation (actually I do).

func1()
{
codeA
somefunc1()
codeB
}

func2()
{
codeA
somefunc2()
codeB
}

func3()
{
codeA
somefunc3()
codeB
}

I'm trying to follow the DRY school of thought and simplify this down
to one function. Sometimes I just use macros, but it's just awful.
Then I got to thinking, gee if only c had closures. Well I did some
searching, and according to wikipedia, you can simulate closures with
just a function pointer that takes a void pointer. The trouble is
those inner functions don't always take the same number of parameters.
I could make arbitrary structures to contain all the parameters into he
void pointer, but in my opinion, making single use structures to lump
arbitrary data together is worse than the macro method.

So, next I tried this.

#include <stdio.h>

void foo1(int a)
{
printf("%d \n",a);
}

void foo2(int a, int b)
{
printf("%d %d\n",a,b);
}

void do_foo(void (*foo)(), int opt)
{
if(opt == 1)
foo(5);
else if(opt == 2)
foo(5,10);
}

int main(void)
{
do_foo(&foo1,1);
do_foo(&foo2,2);
return 0;
}

I'm using opt to choose the function definition. It's a bit ugly, but
I'm ok with it. I'm just not sure if it's legal. I see lots of "don't
ever do this" when I google for unspecified function arguments.

So anyway, c gurus, what is the smart way to handle this? And what do
you think about closures in C, um, 2009 (just kidding)?
 
P

pemo

(e-mail address removed) wrote:

#include <stdio.h>

void foo1(int a)
{
printf("%d \n",a);
}

void foo2(int a, int b)
{
printf("%d %d\n",a,b);
}

void do_foo(void (*foo)(), int opt)
{
if(opt == 1)
foo(5);
else if(opt == 2)
foo(5,10);
}

int main(void)
{
do_foo(&foo1,1);
do_foo(&foo2,2);
return 0;
}

do_foo(&foo1,1);
do_foo(&foo2,2);

&foo1 etc have the type pointer to pointer to function [...], so you should
remove the &.
 
R

Richard Heathfield

pemo said:
(e-mail address removed) wrote:
do_foo(&foo1,1);
do_foo(&foo2,2);

&foo1 etc have the type pointer to pointer to function [...], so you
should remove the &.

I agree that the & is ugly and unnecessary, but you're wrong about the type.

The function pointer foo1 can be expressed as &foo1 too; they are both
"pointer to function". It's a bug-ugly wart, but it's true.
 
P

pemo

Richard said:
pemo said:
(e-mail address removed) wrote:
do_foo(&foo1,1);
do_foo(&foo2,2);

&foo1 etc have the type pointer to pointer to function [...], so you
should remove the &.

I agree that the & is ugly and unnecessary, but you're wrong about
the type.

The function pointer foo1 can be expressed as &foo1 too; they are both
"pointer to function". It's a bug-ugly wart, but it's true.

Thanks for the correction Richard - very interesting! Don't suppose you
have a handy ref into the standard?
 
R

Richard Heathfield

pemo said:
Richard said:
pemo said:
(e-mail address removed) wrote:
do_foo(&foo1,1);
do_foo(&foo2,2);

&foo1 etc have the type pointer to pointer to function [...], so you
should remove the &.

I agree that the & is ugly and unnecessary, but you're wrong about
the type.

The function pointer foo1 can be expressed as &foo1 too; they are both
"pointer to function". It's a bug-ugly wart, but it's true.

Thanks for the correction Richard - very interesting! Don't suppose you
have a handy ref into the standard?

3.2.2.1 of C89 draft.
6.3.2.1[#4] of N869.
6.3.2.1[#4] of C99 final.

Basically, they all say the same thing: an expression of function type is
automagically converted into an expression of type "pointer to function"
/except/ when handed to '&' (or one other edge case, sizeof, which we're
not discussing here); this is because '&' gives you the pointer /anyway/ -
in fact, the conversion is like an invisible implicit '&', which a visible,
explicit '&' replaces.
 
P

pemo

pemo said:
Richard said:
pemo said:
(e-mail address removed) wrote:
do_foo(&foo1,1);
do_foo(&foo2,2);

&foo1 etc have the type pointer to pointer to function [...], so you
should remove the &.

I agree that the & is ugly and unnecessary, but you're wrong about
the type.

The function pointer foo1 can be expressed as &foo1 too; they are
both "pointer to function". It's a bug-ugly wart, but it's true.

Thanks for the correction Richard - very interesting! Don't suppose
you have a handy ref into the standard?

6.5.3.2 - 3

Although the '+' mentioned there has me frowning a bit!
 
G

goldfita

Richard said:
pemo said:
(e-mail address removed) wrote:
do_foo(&foo1,1);
do_foo(&foo2,2);

&foo1 etc have the type pointer to pointer to function [...], so you
should remove the &.

I agree that the & is ugly and unnecessary, but you're wrong about the type.

The function pointer foo1 can be expressed as &foo1 too; they are both
"pointer to function". It's a bug-ugly wart, but it's true.


--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)

Thanks for the tip. Is the code otherwise OK?
 
T

Thad Smith

I love this place. This one problem keeps coming back to bite me.
Suppose I have the following situation (actually I do).

func1()
{
codeA
somefunc1()
codeB
}

func2()
{
codeA
somefunc2()
codeB
}

func3()
{
codeA
somefunc3()
codeB
}

I'm trying to follow the DRY school of thought and simplify this down
to one function. Sometimes I just use macros, but it's just awful.

In order to Don't Repeat Yourself, you sometimes need to create
additional functions or macros. Here are some options to not repeating
codeA and codeB:

1. Make codeA and codeB into separate functions.
func1() {
codeA();
somefunc1();
codeB();
}
etc.

The potential problem with that is that if codeA and codeB share
variables, you have to either pass them as parameters or make them
shared variables.

2. Write wrapper functions to somefuncx() that have a common interface.
wrapsomefunc1(a,b,c,d,e) {
somefunc1(a,b);
}

You can then pass a pointer to the wrapper functions.

3. Pass a selection parameter, as you suggested.
func(int opt) {
codeA;
switch(opt) {
case 0: somefunc1(a,b); break;
case 1: somefunc2(b,c,d,e); break;
case 2: somefunc3(c); break;
}
codeB;
}

This is the tidiest technique, but func() must have the details for all
the options.
 
D

Dave Thompson

I love this place. This one problem keeps coming back to bite me.
Suppose I have the following situation (actually I do).

func1()
{
codeA
somefunc1()
codeB
}

func2()
{
codeA
somefunc2()
codeB
}

[etc.] The trouble is
those inner functions don't always take the same number of parameters.
I could make arbitrary structures to contain all the parameters into he
void pointer, but in my opinion, making single use structures to lump
arbitrary data together is worse than the macro method.

So, next I tried this.

#include <stdio.h>

void foo1(int a)
void foo2(int a, int b)
void do_foo(void (*foo)(), int opt)
{
if(opt == 1)
foo(5);
else if(opt == 2)
foo(5,10);
}

int main(void)
{
do_foo(&foo1,1);
do_foo(&foo2,2);
return 0;
}

I'm using opt to choose the function definition. It's a bit ugly, but
I'm ok with it. I'm just not sure if it's legal. I see lots of "don't
ever do this" when I google for unspecified function arguments.
It is definitely legal as long as the (various) callees take only
'K&R1 compatible' arguments, that is:
- not varargs
- not prototyped parameters narrower than int (flavors of char or
short, or in C99 possibly impl-specific) or double (float)
- and all return the same type (here, void)

and the actual args in the chosen call, after (only) the default
promotions, match the actual parameter types in a prototyped
definition or the promoted types in a nonprototyped definition.

And if you get any of this wrong, either initially or years down the
road after umpteen changes, the implementation is not required to
detect your mistake, and damn few if any will, instead you get
Undefined Behavior which can be arbitrarily bad -- the canonical
example here being nasal demons -- and in this (unlike some other
common UBs) is likely a crash or serious data corruption.

Personally I think that danger outweighs the reduction in code
duplication, which can be achieved by other means if necessary. But
it's your choice. (Or that of your boss/clients/customers/users.)
So anyway, c gurus, what is the smart way to handle this? And what do
you think about closures in C, um, 2009 (just kidding)?

<OT>If I just want some safely shared context I'll use C++ related
classes or function objects, link compatible and (mostly) tool &
practice compatible on all systems of interest to me. If I really want
downward closures I'll probably use Ada or Pascal, generally link
compatible with C and C++. If I actually want upward closures, I've
got more restricted choices.</>

C is what it is. If it ain't broke don't fix it.


- David.Thompson1 at worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top