
Gosh Darn Function Pointers - striking
http://goshdarnfunctionpointers.com/
======
ratboy666
Whatever happened to cdecl?

    
    
      : fred@dejah sql $; cdecl
      Type `help' or `?' for help
      cdecl> declare x as pointer to function returning int
      int (*x)()
      cdecl> declare x as pointer to function returning pointer to function returning int
      int (*(*x)())()
      cdecl>

~~~
ufo
While we are on the topic of cdecl, does anyone know where I can find a
succinct algorithm for producing valid C declarations from a structured
description of the type? Basically, what the examples in the parent post are
doing (and NOT the other way around, which is parsing valid C declarations).

I tried looking at cdecl's source code but everything is implemented as a big
yacc grammar, which obscures the control flow of the algorithm. While this is
necessary for the parsing feature, I suspect that for producing declarations
it might be possible to use a simple recursive algorithm instead...

~~~
imglorp
The algorithm is the "spiral rule".

[http://c-faq.com/decl/spiral.anderson.html](http://c-faq.com/decl/spiral.anderson.html)

~~~
ufo
Thanks, but I actually want the other way around. Instead of parsing an
existing C declaration, I want to produce one when given a structured tree
representing the C datatype.

I found that to be trickier than it sounds.

------
femto113
I've always found a typedef of the function and doing the pointer in the
variable declaration the clearest since it doesn't need funky parentheses or
dereferences anywhere:

    
    
        typedef int func(int arg);
        int foo(int a) { return a * 10; }
        ...
        func *f = foo;
        ...
        int x = f(10);

~~~
B4TMAN
I am a little lost. What effect does `typedef int func(int arg);` have on the
code?

~~~
inopinatus
It allows the later definition of func __* f which is more readable and
maintainable than int ( __* f)(int arg)

This pattern is common in well-structured applications, and is especially
useful for clarifying external and internal APIs. e.g. from the Dovecot IMAP
server:

    
    
        typedef bool event_callback_t(struct event *event,
        			      enum event_callback_type type,
        			      struct failure_context *ctx,
        			      const char *fmt, va_list args);
    

You can now define a function pointer or declare a parameter using
event_callback_t. This is both concise and intention revealing.

------
jitl
See also
[http://fuckingfunctionpointers.com](http://fuckingfunctionpointers.com)

------
inlined
A professor in college explained it in a way that works for me: C is about the
type. When you say "int x" then "x" is an int. When you say "int * x", then "*
x" is an int. Humans figure out the type of x by inverting the operations in
the declaration:

"int (* x[])(char * )" means that x is something you can index into and call
with a string to get an int. OTOH, "int (* x)(char * )[]" is something you can
call with a string and then index into to get an int.

The only gotcha is that function pointers and function names are very stubble
and the compiler lets you be a little fuzzy on saying x[0]("hello, world") or
(*x[0])("hello world")

(forgive spacing. Trying to avoid the formatter)

~~~
dozzie
> OTOH, "int (* x)(char * )[]" is something you can call with a string and
> then index into to get an int.

... _would be_ something you can call if it was a correct declaration. I don't
think it's valid, and certainly GCC in gnu99 mode rejects it (function
returning an array).

The rule I heard is that you start reading the declaration from the variable
name and go right to boundary (parenthesis, comma, or semicolon), then bounce
left and bounce right until all tokens were seen. Then this reads as:

    
    
      int (*x[])(char *)
    

"x is an array <bounce-left> of pointers <bounce-right> to a function that
expects (char pointer) <bounce-right> and returns int".

~~~
Retra
I've heard that rule too, but I've also heard it doesn't work always.

------
blt
My solution: memorize the "function pointer typedef" syntax (second-to-last
element in this list) and use typedefs whenever I use function pointers.

std::function syntax in C++ sure is nice though.

------
smitherfield
NB: If an argument to a function is a function pointer, you can write it like
a normal function declaration.

So you can write

    
    
      void transform_string(char s[], char transform(char));
    

instead of having to write

    
    
      void transform_string(char s[], char(*transform)(char));
    

When writing C++ you should use function _references_ when possible, as
references cannot be null.

    
    
      void transform_string(char s[], char(&transform)(char));
    

Function references unfortunately require the more complex syntax, but
fortunately C++11 `using`-style typedefs are quite simple to use with function
types.

    
    
      using TransformFunc = char(char, char);
      typedef char TransformFunc(char, char); // C-style typedef, same as above
    
      // Function pointer
      TransformFunc fptr = /* ... */
    
      // Function reference
      TransformFunc &fref = /* ... */

~~~
nybble41
_" When writing C++ you should use function references when possible, as
references cannot be null."_

Not true. You can still pass a dereference of a null pointer, which will
create a null reference. The null is detected only when (if) the reference is
used, not when the dereference expression is evaluated as an lvalue for its
address to initialize the reference.

char ( _transform_ptr)(char) = NULL; transform_string(s,_ transform_ptr); //
no exception // ... later, in transform_string() ... transform(...); //
exception

~~~
smitherfield
Yes, that's technically true, but you have to admit a little contrived,
especially as pertaining to function pointers/references.

I'll amend my statement to "unlike pointers, _valid_ references cannot point
to a null address."

This is also true of any other language which has both nullable and non-
nullable references, and a way to convert the former to the latter without
accounting for the null case, such as Rust[1].

    
    
      fn pass_null_reference() {
      	unsafe { print_i32(&*std::ptr::null()) }
      }
    
      fn print_i32(n: &i32) {
      	println!("{}", n) // segfault
      }
    

[1] [https://godbolt.org/g/1oUnNt](https://godbolt.org/g/1oUnNt)

~~~
nybble41
Ah, but what makes it an _invalid_ reference, apart from convention? You could
just as easily say that a null pointer is an invalid pointer in a context
where a non-null value is expected. After all, a reference is just a constant
pointer which is automatically dereferenced. Any program which may contain a
null pointer may also contain a null reference.

 _" This is also true of any other language which has both nullable and non-
nullable references, and a way to convert the former to the latter without
accounting for the null case, such as Rust."_

Indeed, though in Rust (like most other safe-by-default languages) you do need
that glaringly obvious "unsafe" keyword to perform the conversion, which is a
hint that you should be checking for null at that point.

~~~
smitherfield
_> Ah, but what makes it an invalid reference, apart from convention?_

It's an invalid reference in that a reference which does not refer to an
object is either dead code, or an unrecoverable error.

There's no point in checking if a reference refers to null, because (unlike
pointers) it cannot then be assigned a useful value, and also because any such
check will be removed by the optimizer, because null references are undefined
behavior (in both C++[1] and Rust[2]).

 _> Indeed, though in Rust (like most other safe-by-default languages) you do
need that glaringly obvious "unsafe" keyword to perform the conversion, which
is a hint that you should be checking for null at that point._

It is an improvement but (obviously) the unsafety in this case escapes from
the "unsafe" block into the surrounding "safe" code and other "safe"
functions.

It's also possible, even in entirely-"safe" code, to crash a Rust program by
attempting to dereference a null pointer.

    
    
      fn dereference(nullable_ref: Option<&i32>) -> &i32 {
      	nullable_ref.unwrap()
      }
    
      fn crash() {
      	dereference(None);
      }
    

[1] [https://godbolt.org/g/TjexTp](https://godbolt.org/g/TjexTp)

[2] [https://godbolt.org/g/eeXAnn](https://godbolt.org/g/eeXAnn)

~~~
nybble41
_" There's no point in checking if a reference refers to null, because (unlike
pointers) it cannot then be assigned a useful value, and also because any such
check will be removed by the optimizer, because null references are undefined
behavior (in both C++ and Rust)."_

That is a good point; I did not realize it was considered undefined behavior,
which does make it substantially different from a null pointer. (Of course,
that means the compiler remains free to check for null when the reference is
created, which would have been the safer approach. The check would only be
needed when creating a reference from a pointer, and even then it could be
optimized out if the pointer is known to be non-null.)

 _" It is an improvement but (obviously) the unsafety in this case escapes
from the "unsafe" block into the surrounding "safe" code and other "safe"
functions."_

Right. Despite the name, the "unsafe" keyword is not really meant to say that
the code inside the block is unsafe, but rather that the compiler is not
responsible for its safety—it is up to the programmer to expose a safe
interface. This code breaks that rule, and the result is undefined behavior.
The constraint does help in narrowing down the source of the problem to a
small core of functions explicitly marked as unsafe, rather than the entire
codebase.

As steveklabnik pointed out, the safe version (unwrapping a None value)
results in a panic call rather than actually dereferencing a null pointer,
meaning that you can reliably intercept it with std::panic::set_hook—it is
_not_ undefined behavior.

------
inopinatus
Simply declaring a function gives you a pointer to that function. Which
reminds me of the time I wrote this in a PR to FreeBSD:

    
    
        if ((Lflag ? chown : lchown)(p->fts_accpath, -1, s->st_gid))
          /* .. */
    

a line that turned out to be incredibly polarising amongst code reviewers.

Explanation for those not well versed in C: the ternary in the left-hand
parentheses is selecting which syscall wrapper function will be dereferenced
and called with the arguments in the right-hand parentheses. It relies on the
chown(2) and lchown(2) syscall wrappers having identical function signatures.

~~~
edcarter
That is fancy, but not very readable. I would have been on the side of the
code review which would favor readability over conciseness. Cool trick though.

------
Randdalf
I've found using a macro makes function pointers really easy. Learnt this from
Handmade Hero:

    
    
      #define SOME_FUNCTION(name) void name(int Arg0, float Arg1)
    
      typedef SOME_FUNCTION(some_function);
    
      SOME_FUNCTION(SomeFunction)
      {  
      }
    
      struct some_functions
      {
          some_function* Foo;
          some_function* Bar; 
      };
    
    
      // Either works here.
      some_functions SomeFunctions;
      SomeFunctions.Foo = &SomeFunction;
      SomeFunctions.Bar = SomeFunction;

------
jerryr
Hi HN! You found my site again. Here's the previous discussion from almost
exactly year ago (warning, this links to the profane version):
[https://news.ycombinator.com/item?id=13437182](https://news.ycombinator.com/item?id=13437182)

------
mikevm
This is how I learned to read function pointer declarations:
[http://cseweb.ucsd.edu/~ricko/CSE131/rt_lt.rule.html](http://cseweb.ucsd.edu/~ricko/CSE131/rt_lt.rule.html)

------
kazinator
Also, as a parameter to a function, alternative:

    
    
      int my_function(returnType parameterName (parameterTypes));
    

When you apparently declare a function parameter of function type, the type is
adjusted to pointer-to-function, similarly to the way array types as
parameters are pointers.

This style is useful for de-cluttering code that has a lot of functional
parameters all over the place.

------
neonscribe
"(but try not to cast functions)". Try not to cast at all, of course, but
especially not functions.

~~~
geofft
I was going to post something about how you need to cast function pointers to
use dlsym(), which returns a void * (since it doesn't know statically what the
type of function you're asking for is, let alone that it's a function at all).
You'd do something like

    
    
        void *mylib = dlopen("mylib.so");
        int (*myfn)(int) = (int (*)(int))dlsym(mylib, "myfn");
    

And I was also going to comment on how casts between data pointers (including
void * ) and function pointers are undefined behavior in the C spec, but
conformance to POSIX requires that to work because how else are you going to
use dlsym. But then I looked up the example in the POSIX.1-2004 dlsym manpage,
which does this bizarre thing instead:
[http://pubs.opengroup.org/onlinepubs/009695399/functions/dls...](http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html)

    
    
        int (*fptr)(int);
        *(void **)(&fptr) = dlsym(handle, "my_function");
    

and there's a RATIONALE section in that man page explaining why, and the Linux
dlsym man page calls it out too: [http://man7.org/linux/man-
pages/man3/dlopen.3.html#EXAMPLE](http://man7.org/linux/man-
pages/man3/dlopen.3.html#EXAMPLE)

    
    
       /* According to the ISO C standard, casting between function
          pointers and 'void *', as done above, produces undefined results.
          POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and
          proposed the following workaround:
        
              *(void **) (&cosine) = dlsym(handle, "cos");
        
          This (clumsy) cast conforms with the ISO C standard and will
          avoid any compiler warnings.
        
          The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.
          POSIX.1-2013) improved matters by requiring that conforming
          implementations support casting 'void *' to a function pointer.
          Nevertheless, some compilers (e.g., gcc with the '-pedantic'
          option) may complain about the cast used in this program. */
    

and indeed, the POSIX.1-2013 man page does the natural thing, saying "This
standard requires this conversion to work correctly on conforming
implementations":
[http://pubs.opengroup.org/onlinepubs/9699919799/functions/dl...](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html)

I wonder if there are any C compilers that actually optimize casts between
data and function pointers as if they could be undefined (not counting
hardware architectures where such casts literally don't work because they're
in different address spaces).

~~~
neonscribe
What sort of optimization might be possible? Also, is there any active
development today on separate instruction and data address space
architectures?

~~~
kragen
It probably no longer makes sense to use AVRs instead of ARMs (or MIPS or
whatever) for cost reasons, but there are still a lot of things out there that
use them for backward-compatibility reasons — most notably most Arduinos — and
lots of people are still doing active development for those things, and
probably will be until the mid-2020s, if not later. People are still building
new 8051-based systems, for heaven's sake, although not using discrete 8051
chips.

------
sebazzz
I cannot imagine how complex a parser for C must look like.

~~~
tannhaeuser
C's type expressions can actually be parsed by a simple operator-precedence
parser, and implementing one is very helpful in understanding C expressions.
As they say, to understand a language, write a compiler (or at least a parser)
for it.

~~~
kazinator
Years ago, after I wrote an emulator for the MC68000 (68010 actually), I
suddenly became very good at coding in MC68K assembly language. Not
necessarily in better in organizing big assembly language programs, but just
the raw coding of small blocks of code well.

