<<< Date Index >>>     <<< Thread Index >>>

Re: 1.5.8 build failure on Solaris 8



Brendan Cully writes:
> I don't suppose you could dig a little further into exactly what's
> causing 'open' to get rewritten?

(Sorry for the late response -- I've been rather ill recently.)

This is actually entirely in accordance with the relevant standards.
The rule is that any function described in the library clause of the C
standard (clause 7 in ISO C89) must exist as a function (so that you can
take a pointer to it), but the implementation is entirely free to shadow
the declaration of the function with a macro that has exactly the same
effect.  Now, C itself doesn't actually have a function called 'open',
but POSIX and Single Unix (which do) adopt the same rule that
implementations are free to define an equivalent macro.  (See
http://www.opengroup.org/onlinepubs/007908799/xsh/interfaces.html, for
example.)

This sort of situation (a struct containing a function pointer named
after a standard function) tends to bite people fairly often.  Defining
the struct doesn't cause a problem:

  typedef int (*Function)(const char *, int);
  struct s {
    Function open;
    int (*close)(int);
  };

Why not?  Because the rule is that function-like macros only get
expanded when the name is followed immediately by (optional whitespace
and) an open paren.  Regardless of whether you use an extra typedef name
for the function-pointer type, you don't get that in the struct
definition.

The problem only crops up when you try to use the function:

  struct s *p = ...;
  int fd = p->open(name, mode);   /* ??? */
  p->close(fd);                   /* ??? */

Now we do have a form which permits function-like macros to be expanded,
and thus the problem.

Fixes?  Renaming the members of the struct is probably the best option.

There's no point trying to find a compilation mode on some particular
combination of OS and compiler which doesn't trigger this behaviour,
because any other implementation could do the same thing.

Another possibility is to

  #undef open

(and so on for other such names) when defining the struct.  This leads
to nasty behaviour, though; a file that does

  #include <unistd.h>
  #include "mutt_socket.h"

will work, though one that does

  #include "mutt_socket.h"
  #include <unistd.h>

will fail.

The only other option is unpleasant: change all calls to look like this:

  struct s *p = ...;
  int fd = (p->open)(name, mode);
  (p->close)(fd);

This has the simple syntactic effect of preventing the calls from
looking like calls to function-like macros -- but at the high cost of
obscuring every single call made through such a struct, not to mention
the difficulty of remembering to do this.

-- 
Aaron Crane