Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C Programming (http://www.velocityreviews.com/forums/f42-c-programming.html)
-   -   Function factories (http://www.velocityreviews.com/forums/t951198-function-factories.html)

Edward Rutherford 08-21-2012 08:36 PM

Function factories
 
Hello

I want to make a factory that produces other functions according to a
parameter.

The following code does not compile:

typedef int *(foo)(int);

foo adder_factory(int x)
{
int f(int a)
{
return a + x;
}
return &f;
}

The idea is that adder_factor(42) should be a function that adds 42 to
whatever it gets passed.

What's the right way of doing this in C? How can I get closures or
something similar in C?

Nobody 08-21-2012 08:55 PM

Re: Function factories
 
On Tue, 21 Aug 2012 20:36:31 +0000, Edward Rutherford wrote:

> I want to make a factory that produces other functions according to a
> parameter.
>
> The following code does not compile:
>
> typedef int *(foo)(int);
>
> foo adder_factory(int x)
> {
> int f(int a)
> {
> return a + x;
> }
> return &f;
> }


C does not have inner functions or closures.

> The idea is that adder_factor(42) should be a function that adds 42 to
> whatever it gets passed.
>
> What's the right way of doing this in C? How can I get closures or
> something similar in C?


typedef struct foo {
int (*func)(const struct foo *, int);
int param;
} *foo;

static int adder(const struct foo *closure, int x) {
return closure->param + x;
}

foo adder_factory(int x)
{
foo p = malloc(sizeof(*p));
p->func = adder;
p->param = x;
return p;
}

#define call_foo(f, x) ((f)->func((f), (x))



Nick Keighley 08-22-2012 08:52 AM

Re: Function factories
 
On Aug 22, 7:34*am, David Brown <da...@westcontrol.removethisbit.com>
wrote:
> On 21/08/2012 22:36, Edward Rutherford wrote:
>
>
>
>
>
> > Hello

>
> > I want to make a factory that produces other functions according to a
> > parameter.

>
> > The following code does not compile:

>
> > typedef int *(foo)(int);

>
> > foo adder_factory(int x)
> > {
> > * *int f(int a)
> > * *{
> > * * *return a + x;
> > * *}
> > * *return &f;
> > }

>
> > The idea is that adder_factor(42) should be a function that adds 42 to
> > whatever it gets passed.

>
> > What's the right way of doing this in C? How can I get closures or
> > something similar in C?

>
> You can't.
>
> You can get sort-of simulated closures in complicated and verbose ways,
> but there is no good way to do it. *gcc supports nested functions as an
> extension to C, but that won't give you closures like this.
>
> If you want to stick close to C, then C++11 supports lambda functions
> and closures.
>
> If you want to write code that makes strong use of closures in the
> style, then use a programming language that supports it. *The latest C++
> standard will get you the basics, but if you want to write code in a
> more functional programming style, there are much better languages to
> work with (ocaml, python, haskell, etc.)


scheme

(and does Python have particularly good support for functional
programming?)

jacob navia 08-22-2012 10:32 AM

Re: Function factories
 
Le 21/08/12 22:36, Edward Rutherford a écrit :
> Hello
>
> I want to make a factory that produces other functions according to a
> parameter.
>
> The following code does not compile:
>
> typedef int *(foo)(int);
>
> foo adder_factory(int x)
> {
> int f(int a)
> {
> return a + x;
> }
> return &f;
> }
>
> The idea is that adder_factor(42) should be a function that adds 42 to
> whatever it gets passed.
>
> What's the right way of doing this in C? How can I get closures or
> something similar in C?
>


If you have the JIT of lcc-win you write:

typedef int (*foo)(int);


foo adder_factory(int value)
{
foo fnPtr;
char buf[512];

sprintf(buf,"int fn(int a) { return a + %d;}",value);
fnPtr = (foo)compile(buf);
return fnPtr;
}



tom st denis 08-22-2012 02:40 PM

Re: Function factories
 
On Aug 22, 6:32*am, jacob navia <ja...@spamsink.net> wrote:
> Le 21/08/12 22:36, Edward Rutherford a écrit :
>
>
>
>
>
>
>
>
>
> > Hello

>
> > I want to make a factory that produces other functions according to a
> > parameter.

>
> > The following code does not compile:

>
> > typedef int *(foo)(int);

>
> > foo adder_factory(int x)
> > {
> > * *int f(int a)
> > * *{
> > * * *return a + x;
> > * *}
> > * *return &f;
> > }

>
> > The idea is that adder_factor(42) should be a function that adds 42 to
> > whatever it gets passed.

>
> > What's the right way of doing this in C? How can I get closures or
> > something similar in C?

>
> If you have the JIT of lcc-win you write:
>
> typedef int (*foo)(int);
>
> foo adder_factory(int value)
> {
> * * * * foo fnPtr;
> * * * * char buf[512];
>
> * * * * sprintf(buf,"int fn(int a) { return a + %d;}",value);
> * * * * fnPtr = (foo)compile(buf);
> * * * * return fnPtr;


You could accomplish similiar [though more longwinded] via compiling
code and using dlopen() to load it into your process space.

Both of which are completely OT for this group though.

BTW: your code doesn't seem to do any sort of error checking. What if
compile() fails?

Tom

James Kuyper 08-22-2012 03:05 PM

Re: Function factories
 
On 08/22/2012 10:40 AM, tom st denis wrote:
> On Aug 22, 6:32 am, jacob navia <ja...@spamsink.net> wrote:

....
>> If you have the JIT of lcc-win you write:
>>
>> typedef int (*foo)(int);
>>
>> foo adder_factory(int value)
>> {
>> foo fnPtr;
>> char buf[512];
>>
>> sprintf(buf,"int fn(int a) { return a + %d;}",value);
>> fnPtr = (foo)compile(buf);
>> return fnPtr;

>
> You could accomplish similiar [though more longwinded] via compiling
> code and using dlopen() to load it into your process space.


The intent was for the definition of the adder to be contained inside
the definition of adder_factory().

> Both of which are completely OT for this group though.
>
> BTW: your code doesn't seem to do any sort of error checking. What if
> compile() fails?


From the way it's used, it would appear to return a pointer. If
compile() fails, it might return a null pointer, in which case
adder_factory() will also return a null pointer, leaving it for the
caller to decide what to do about it. That's a perfectly reasonable way
of passing on the information that the call failed.

However, I think that compile() looks horribly type-unsafe.

Edward Rutherford 08-22-2012 04:36 PM

Re: Function factories
 
Nobody wrote:
> On Tue, 21 Aug 2012 20:36:31 +0000, Edward Rutherford wrote:
>
>> I want to make a factory that produces other functions according to a
>> parameter.
>>
>> The following code does not compile:
>>
>> typedef int *(foo)(int);
>>
>> foo adder_factory(int x)
>> {
>> int f(int a)
>> {
>> return a + x;
>> }
>> return &f;
>> }

>
> C does not have inner functions or closures.
>
>> The idea is that adder_factor(42) should be a function that adds 42 to
>> whatever it gets passed.
>>
>> What's the right way of doing this in C? How can I get closures or
>> something similar in C?

>
> typedef struct foo {
> int (*func)(const struct foo *, int); int param;
> } *foo;
>
> static int adder(const struct foo *closure, int x) {
> return closure->param + x;
> }
>
> foo adder_factory(int x)
> {
> foo p = malloc(sizeof(*p));
> p->func = adder;
> p->param = x;
> return p;
> }
>
> #define call_foo(f, x) ((f)->func((f), (x))


Thanks for the suggestion. Unfortunately I need an actual function, not a
macro, to be able to pass as a comparator function to qsort.

Guess I just have to communicate what I need through a global variable?
It would be nice if qsort took an extra "void *data" parameter, like
functions in Glib (where it's called a gpointer of course)...

James Kuyper 08-22-2012 05:01 PM

Re: Function factories
 
On 08/22/2012 12:36 PM, Edward Rutherford wrote:
> Nobody wrote:
>> On Tue, 21 Aug 2012 20:36:31 +0000, Edward Rutherford wrote:

....
>>> The idea is that adder_factor(42) should be a function that adds 42 to
>>> whatever it gets passed.
>>>
>>> What's the right way of doing this in C? How can I get closures or
>>> something similar in C?

>>
>> typedef struct foo {
>> int (*func)(const struct foo *, int); int param;
>> } *foo;
>>
>> static int adder(const struct foo *closure, int x) {
>> return closure->param + x;
>> }
>>
>> foo adder_factory(int x)
>> {
>> foo p = malloc(sizeof(*p));
>> p->func = adder;
>> p->param = x;
>> return p;
>> }
>>
>> #define call_foo(f, x) ((f)->func((f), (x))

>
> Thanks for the suggestion. Unfortunately I need an actual function, not a
> macro, to be able to pass as a comparator function to qsort.
>
> Guess I just have to communicate what I need through a global variable?


That's one alternative. However, you can avoid creating a global, with
all of the attendant problems. When used correctly, qsort() never calls
the comparison function with null pointers. You can take advantage of
that fact by making x a static variable local to the comparison
function, and if the first argument to that function is a null pointer,
interpret the second argument as a pointer to a new value for x.

> It would be nice if qsort took an extra "void *data" parameter, like
> functions in Glib (where it's called a gpointer of course)...


Yes. It's a well-known problem with all of the original C standard
library's callback functions. I think C11 has introduced some new
callbacks, but I haven't had time to learn much about them.




Edward Rutherford 08-22-2012 06:33 PM

Re: Function factories
 
James Kuyper wrote:

> On 08/22/2012 12:36 PM, Edward Rutherford wrote:
>> Nobody wrote:
>>> On Tue, 21 Aug 2012 20:36:31 +0000, Edward Rutherford wrote:

> ...
>>>> The idea is that adder_factor(42) should be a function that adds 42
>>>> to whatever it gets passed.
>>>>
>>>> What's the right way of doing this in C? How can I get closures or
>>>> something similar in C?
>>>
>>> typedef struct foo {
>>> int (*func)(const struct foo *, int); int param;
>>> } *foo;
>>>
>>> static int adder(const struct foo *closure, int x) {
>>> return closure->param + x;
>>> }
>>>
>>> foo adder_factory(int x)
>>> {
>>> foo p = malloc(sizeof(*p));
>>> p->func = adder;
>>> p->param = x;
>>> return p;
>>> }
>>>
>>> #define call_foo(f, x) ((f)->func((f), (x))

>>
>> Thanks for the suggestion. Unfortunately I need an actual function, not
>> a macro, to be able to pass as a comparator function to qsort.
>>
>> Guess I just have to communicate what I need through a global variable?

>
> That's one alternative. However, you can avoid creating a global, with
> all of the attendant problems. When used correctly, qsort() never calls
> the comparison function with null pointers. You can take advantage of
> that fact by making x a static variable local to the comparison
> function, and if the first argument to that function is a null pointer,
> interpret the second argument as a pointer to a new value for x.


That is very clever.

It does feel like a little bit of a hack to me, and I'd probably want to
comment what was going on quite carefully.

It also has the downside of introducing an extra branch instruction
(check against NULL) in a function that could be called many many times
in a sort.

Still, ingenious! Thanks.

James Kuyper 08-23-2012 02:26 AM

Re: Function factories
 
On 08/22/2012 01:01 PM, James Kuyper wrote:
> On 08/22/2012 12:36 PM, Edward Rutherford wrote:

....
>> It would be nice if qsort took an extra "void *data" parameter, like
>> functions in Glib (where it's called a gpointer of course)...

>
> Yes. It's a well-known problem with all of the original C standard
> library's callback functions. I think C11 has introduced some new
> callbacks, but I haven't had time to learn much about them.


The new callback function is the one passed to
set_constraint_handler_s(). You cannot pass set_constraint_handler_s() a
corresponding void*data to be passed to the constraint handler when it
is called, so it looks like the committee has opted for consistency with
previous callback functions, rather than more flexible functionality.

constraint_handler_t functions take a second argument that is a void*,
but it is either null or points at an implementation-defined object,
which means that portable code can't do anything more useful with that
pointer than reporting whether or not it's null (which isn't
spectacularly useful).
--
James Kuyper


All times are GMT. The time now is 08:38 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.