I am trying to create a function that will be generally used to determine if a file/directory exists, if it is within a certain path, and if it is of the specified type (S_ISREG, S_ISDIR, …
Here are the parts of the source pertaining to this question:
int checkExists(const char *name, void *func(int))) {
struct stat st ;
int returnValue = DOES_NOT_EXIST ;
BOOL isInSourceHierarchy, isInTargetHierarchy ;
if (stat(name, &st) != -1) {
returnValue = EXISTS ;
if (memcmp(name, top_dir, strlen(top_dir)))
returnValue |= IN_SOURCE_STRUCTURE ;
if (memcmp(name, tgt_dir, strlen(tgt_dir)))
returnValue |= IN_TARGET_STRUCTURE ;
if (func != NULL) {
if (objType == func(st.st_mode))
returnValue |= RIGHT_TYPE ;
}
return returnValue ;
}
}
. . .
if (S_ISREG(0)) {} // Yes, I know, I know. This is for demonstration only.
if (checkExists(old, S_ISREG)) {
// Do something
} else { // Old file doesn't exist
// Do something else
}
When trying to compile this, I get:
linkStructure.c:642:31: error: ‘S_ISREG’ undeclared (first use in this function)
642 | if (checkExists(old, S_ISREG)) {
| ^~~~~~~
As can be seen, there is no complaint about the macro used as designed, of course. So my question is: Is there a way for me to reference the macro in my source like this? Or will I be left to basically emulate the macro in my function? (I suppose I could pass the expected type rather than the function, but for the sake of clarity, I would like to understand this limitation.)
1
It says S_ISREG
is undefined because the preprocessor doesn’t ignore the parenthesis. #define FOO()
defines FOO()
, not FOO
.
Also, your function expects a function pointer as parameter, but macros are compile-time only symbols, you can’t take its address.
But you can just wrap it in a function instead:
int isReg(int mode)
{
return S_ISREG(mode);
}
Now you can use it as you wanted:
checkExists(old, isReg);
And you keep the flexibility of your original code.
No, you can’t do what you are trying to achieve.
S_ISREG
doesn’t exist, and there’s no telling to what S_ISREG(x)
expands.
Remember that macros simply perform token substitution, which means that S_ISREG(x)
gets replaced with different code before parsing even begins (in theory). Say it expands to something like ((x) & S_IFREG)
. How can you pass that to the your function? You’d have to create a function from it.
typedef int ModeCheck( int );
int s_isreg( int mode ) {
return S_ISREG( mode );
}
int check_exists( const char *qfn, ModeCheck mode_check ) {
...
if ( mode_check( st.st_mode ) ) {
...
}
...
}
check_exists( qfn, s_isreg );
Alternatively, you could pass S_IFREG
(which expands to a mask) and do the appropriate bit operation yourself.
int check_exists( const char *qfn, int mode_mask ) {
...
if ( ( st.st_mode & S_IFMT ) == mode_mask ) ) ) {
...
}
...
}
check_exists( qfn, S_IFREG );
3
The S_ISREG
macro is a function-like macro, so simply using it as S_ISREG
is invalid.
You’ll need to instead pass in the flag for the mode in question, i.e. S_IFREG
, an perform a bitmask with that and the file mode.
int checkExists(const char *name, int ftype) {
struct stat st ;
int returnValue = DOES_NOT_EXIST ;
BOOL isInSourceHierarchy, isInTargetHierarchy ;
if (stat(name, &st) != -1) {
returnValue = EXISTS ;
if (memcmp(name, top_dir, strlen(top_dir)))
returnValue |= IN_SOURCE_STRUCTURE ;
if (memcmp(name, tgt_dir, strlen(tgt_dir)))
returnValue |= IN_TARGET_STRUCTURE ;
if ((st.st_mode & S_IFMT) == ftype)
returnValue |= RIGHT_TYPE ;
return returnValue ;
}
}
. . .
if (checkExists(old, S_IFREG)) {
// Do something
} else { // Old file doesn't exist
// Do something else
}