Hi
Why put * in front of the function name in the declaration?
int (*write)(struct inode *, struct file *, const char *, int);
thanks
from Peter (
[email protected])
This declares write as a pointer to a function taking 4 arguments and
returning an int. As to *why* you want to do this, there are a couple
of reasons:
1. You can pass a pointer to a function as a parameter to another
function; this is known as a callback, and is useful in a number of
contexts. The canonical example is the qsort() library function, which
can sort 1D arrays elements of any type. Here's how it's used:
/*
* prototype for qsort() as found in stdlib.h
*
* void qsort(void *base, size_t count, size_t size, int (*cmp)(const
void *, const void *));
*/
#include <stdlib.h>
#include <string.h>
int compareInts(const void *arg1, const void *arg2)
{
int *a = arg1;
int *b = arg2;
if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else
return 0;
}
int compareMyStructs(const void *arg1, const void *arg2)
{
MyStruct *a = arg1;
MyStruct *b = arg2;
/*
* Assume the key value for MyStruct is a character string
*/
char *key1 = a->getKey();
char *key2 = b->getKey();
if (strcmp(a, b) < 0)
return -1;
else if (strcmp(a, b) > 0)
return 1;
else
return 0;
}
int main(void)
{
int iArr[20];
MyStruct sArr[30];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0],
compareInts);
qsort(sArr, sizeof sArr/sizeof sArr[0], sizeof sArr[0],
compareMyStructs);
...
return 0;
}
The signal() library function does something similar; you pass it a
pointer to a function you want executed every time a particular signal
is raised:
void handleInterrupt(sig_atomic_t sig)
{
gInterrupt = 1;
}
int main(void)
{
signal(SIGINT, handleInterrupt);
...
}
2. With function pointers, you can implement a primitive form of
polymorphism, where you can refer to a generic "write" function in your
logic that resolves to one of several different functions based at
runtime. One project I did many years ago involved writing parsers for
a set of data files. I created a lookup table that was keyed by file
type and contained pointers to the actual parse functions, and a
function that performed the actual lookup. It was structured somewhat
like this:
extern void parseGraFile(char *fileName);
extern void parsePWaveFile(char *fileName);
....
struct parseTable {
char *fileType;
void (*parse)(char *fileName);
};
static struct parseTable theTable[] = {{"GRA", parseGraFile}, {"PWV",
parsePWaveFile}, ...}
/*
* The following declaration reads as "lookup is a function returning a
pointer to a function
* taking a char * parameter and returning void"
*/
void (*lookup(char *fileType))(char *fileName)
{
size_t tableCount = sizeof theTable/sizeof theTable[0];
size_t i;
for (i = 0; i < tableCount; i++)
{
if (!strcmp(fileType, theTable
.fileType))
return theTable.parse;
}
return NULL;
}
char *typeFromName(char *fileName)
{
/* parse the file type out of the file name and return it */
}
void parseFiles(const char **fileList, const size_t numFiles)
{
size_t i;
for (i = 0; i < numFiles; i++)
{
char *type;
void (*parser)(char *fileName);
type = typeFromName(fileList);
parser = lookup(type);
if (parser)
{
parser(fileList);
}
else
{
/* handle unrecognized file type */
}
}
}
Basically, the right parsing function would be called based on the file
type. The advantage to this approach is that you don't have to hack
the main application logic every time a new file type is added or
removed; you just update the lookup table.