tags:

views:

1540

answers:

5

As the title already states, I'm trying to declare a nested function and return a pointer to that function. I want this function 'not' to return a new function pointer which will return the negation of whatever the original function was.

Here is what I have:

someType not( someType original ) {
    int isNot( ListEntry* entry ) {
        return !original( entry );
    }

    someType resultFunc = calloc( 1024, 1 );
    memcpy( resultFunc, &isNot, 1024 );

    return resultFunc;
}

someType is defined as:

typedef int(*someType)(ListEntry* entry)
A: 

I'm using GCC.

You can turn on nested functions by using the flag:

-fnested-functions

when you compile.

Steve Willard
A: 

I also never heard of nested functions in C, but if gcc supports it, this is not going to work the way you expect. You are just simply copying the machine instructions of isNot, and that won't include the actual value of "original" at the time "not" is being called.

You should use a C++ class to implement a function object that stores a pointer that you can initialize with the value of "original" and return an instance of this class from "not".

Jim Buck
+14  A: 

Steve, you have a completely wrong mental model of what is a C function.

someType resultFunc = calloc( 1024, 1 );
memcpy( resultFunc, &isNot, 1024 );

From your code fragment, I can surmise that you think that you can copy function's compiled code into a block of memory, and then reuse it. This kind of thing smells of Lisp, except even in lisp you don't do it that way.

In fact, when you say "&isNot", you get a pointer to function. Copying the memory that pointer points at is counterproductive - the memory was initialized when you loaded your executable into memory, and it's not changing. In any case, writing someFunc() would cause a core dump, as the heap memory behing someFunc cannot be executed - this protects you from all sorts of viruses.

You seem to expect an implementation of closures in C. That implementation is simply not there. Unlike Lisp or Perl or Ruby, C cannot preserve elements of a stack frame once you exited that frame. Even is nested functions are permitted in some compilers, I am sure that you cannot refer to non-global variables from inside those functions. The closes thing to closures is indeed C++ object that stores the state and implements operator(), but it's a completely different approach, and you'd still have to do things manually.

Update: here is the relevant portion of GCC documentation. Look for "But this technique works only so long as the containing function (hack, in this example) does not exit."

Arkadiy
thanks for the link, my favorite quote was "If you try to call the nested function through its address after the containing function has exited, all hell will break loose."
luke
+1  A: 

You're not going to be able to do this in the fashion you want. You have a couple of alternative options.

You can use macros:

#define FN_NOT(F) !F
#define notSomeFunc FN_NOT(someFunc)
...
x = notSomeFunc(entry);

But I suspect you wanted to be able to pass the negated function around to other functions that take function pointers, so that won't work.

You can change your interfaces to accept some extra information, eg

struct closure {
  void *env;
  int (*f)(struct closure* extra, ListEntry*);
};

static int isNot(struct closure* extra, ListEntry *entry) {
  someType original = extra->env;
  return !original(entry);
}

struct closure not(someType original) {
   closure rv;
   rv.env = original;
   rv.f = &isNot;
   return rv;
}

And then use it like:

struct closure inverse_fn;
inverse_fn = not( &fn );
if( inverse_fn.f(&inverse_fn, entry) ) {
    ...
}

There are other things you can try, like JITing functions at runtime, but those sorts of techniques are going to be platform and architecture dependent. This solution is awkward, but pure C and portable.

Logan Capaldo
A: 

I think this question would make a great addition to the C infrequently asked questions list.

Artelius