8

I'm trying to decipher this declaration from sqlite3.c

SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);

It seems like it is declaring a function because subsequently there is this

SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *pVfs, void *pHdle, const char *zSym))(void){
  return pVfs->xDlSym(pVfs, pHdle, zSym);
}

and then what appear to be calls to the function

xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);

and

xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);

But I cannot make sense of the declaration. I've highlighted what I cannot understand

SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);
                    ^                                                    ^^^^^^^

I'm wondering why the declaration is not like so

SQLITE_PRIVATE void *sqlite3OsDlSym(sqlite3_vfs *, void *, const char *);

I expect that there may well be a similar question already asked but searching for terms like (, ) and void doesn't really get anywhere. So, if this is a dupe, I'd be very happy for it to be closed as such.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490

2 Answers2

8

This is declaring a function that returns a function pointer. The return type is void (*)(void) (SQLITE_PRIVATE expands to static and is not part of the return type, per comments), but the function name (and parameters) have to appear inside the (*) part.

Functions and arrays are the two C type categories that require to do gymnastics to parse. You can think of array types and function types as "decorating" the identifier that they describe. If you write int foo, you're saying that the symbol "foo" has an integer type. If you write int foo(double), you're saying that the symbol foo(double) has an integer type. foo and (double) have to stick together, so any further decoration has to wrap the entire thing as if it was a single name.

The point is best illustrated by mixing array types and function types, even though the end type might not be legal in C. (That says a lot about how ridiculous the syntax is.) For instance:

int foo[5](double)

would be an array (foo[5]) of functions (int ... (double)). On the other hand:

int foo(double)[5]

is a function (foo(double)) and returns an array (int ... [5]).

The external resource cdecl.org can help you make sense of this kind of declaration. However, you need to replace the structure names with standard types (or write structure types as struct sqlite_vfs instead of just sqlite_vfs) for it to understand your declaration.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • Thanks, I tried cdecl.org, but couldn't get it to give me anything other than "syntax error". I can see now that was because it did not recognise `sqlite3_vfs`. If I'd just replaced `sqlite3_vfs` with `int` then I would have had my answer. – David Heffernan Aug 15 '17 at 21:11
  • And the other thing that caught me out I suppose is that although `sqlite3OsDlSym` returns "pointer to function (void) returning void", the actual function pointer returned has different prototype, hence the `(sqlite3_loadext_entry)` cast. – David Heffernan Aug 15 '17 at 21:15
  • Depending on what `SQLITE_PRIVATE` actually is, it may apply to the function being declared rather than the return type (e.g. if it were a macro for `static`) – M.M Aug 15 '17 at 21:21
  • `SQLITE_PRIVATE` expands to `static` I believe – David Heffernan Aug 15 '17 at 21:23
2

This declaration

SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);

is a declaration of a function that has the return type that is function pointer of the type

void ( * )( void )

The declaration can be simplified by introducing a typedef. For example

typedef void ( *FP )( void );

SQLITE_PRIVATE  FP sqlite3OsDlSym(sqlite3_vfs *, void *, const char *);

Here is a demonstrative program that uses a similar function that returns a pointer to another function.

#include <stdio.h>

typedef void ( *FP )( void );

void h( void )
{
    puts( "Hello World" );
}

FP f( void );
void ( *f( void) )( void )
{
    return h;
}

int main(void) 
{
    f()();

    return 0;
}

Its output is

Hello World

At first the function f is declared using a typedef and then without the typedef

FP f( void );
void ( *f( void) )( void )
{
    return h;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335