9

I am writing multiplatform code which needs to use a pointer to setjmp/sigsetjmp. Normally that would be as simple as doing

#include <setjmp.h>
void * sigsetjmp_p = sigsetjmp;

However, ISO and POSIX state that setjmp/sigsetjmp can be defined as a macro, and indeed that is the case in my linux box. Here's an excerpt from /usr/include/setjmp.h:

# define sigsetjmp(env, savemask)       __sigsetjmp (env, savemask)

The problem is that, since I am not passing arguments to sigsetjmp, the macro doesn't expand and the plain sigsetjmp symbol is not defined in libc. I was hoping to be able to use some macro "black magic" to extract the "__sigsetjmp" name, but I have failed miserably so far.

Another option would be using __sigsetjmp directly but this would imply checking the expansion for every supported platform, which I don't want to do (hence the reason of this question).

PS: I hate macros.

Note:

The reason for which I need this is a bit obscure, but to simplify it, let's say I want to perform pointer comparisons with it.

#include <setjmp.h>
int equals_sigsetjmp(void *p)
{
 void * sigsetjmp_p =  sigsetjmp;
 return p == sigsetjmp_p;
}

Edit:

Yes, yes. I know I shouldn't count on getting a pointer to sigsetjmp because it might not even be a function in certain platforms but this doesn't solve my problem.

In practice, all platforms I know of implement it as a function.

I can cope with the fact that, in a few years, I run into a case in which sigsetjump is not a function for certain platform. But what I wouldn't like to cope with is going through every supported platform and check setjmp.h for macro definitions, which is my only option right now.

I appreciate the references to standards but I would like to get a practical answer not a purist one.

fons
  • 4,905
  • 4
  • 29
  • 49
  • 2
    Btw., casting a function pointer to a `void*` is also non-portable. – Fred Foo Mar 06 '13 at 15:08
  • There is a real reason for this, but it is rather long to explain (it involves function wrapping). Trust me, I need that pointer :) – fons Mar 06 '13 at 15:08
  • @larsmans true, I just wanted to provide a simplified example. Giving a proper type to p though doesn't change the problem. – fons Mar 06 '13 at 15:13
  • 1
    Alas, `setjmp`/`sigsetjmp` is allowed to be a macro precisely because it may not be feasible to implement it as a function. It could expand to a bunch of `asm` commands, a builtin, or whatever else the implementation desires. You cannot count on getting a pointer to it. – n. m. could be an AI Mar 06 '13 at 15:17

3 Answers3

2

You can't do this portably, as the standard explicitly forbids it (§7.13):

It is unspecified whether setjmp is a macro or an identifier declared with external linkage. If a macro definition is suppressed in order to access an actual function (...) the behavior is undefined.

To which POSIX adds that

The sigsetjmp() function shall be equivalent to the setjmp() function

with some exceptions that are irrelevant here.

As for your remark

all platforms I know of implement it as a function

In GCC, setjmp is a built-in, and I believe in Clang, so is sigsetjmp. (Though the C library might have a function version as well.)

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • Thanks for the reference to the standard. You are right, I shouldn't rely on the actual function but unfortunately I need to. And in practice it won't lead to undefined behavior for all the platforms we support. – fons Mar 06 '13 at 15:28
  • @fons: if you have a limited set of platforms to support and you want to perform black magic, then the solution is to maintain a list of `#ifdef`s that handle the cases you're interested in. But beware that the behavior is still undefined "in practice" because a new version of the compiler or library may break your program. You're effectively tying yourself to a compiler version, library version, and a set of compiler flags that make your program work. – Fred Foo Mar 06 '13 at 15:32
  • Can you show proof of `setjmp` being a builtin in GCC? I can clearly see it being declared as a function (without macros) in /usr/include/sejmp.h. `extern int setjmp (jmp_buf __env) __THROWNL;` – fons Mar 06 '13 at 15:33
  • @fons: try replacing `setjmp` with `__builtin_setjmp`. Apparently, GNU libc provides a function `setjmp` as well, probably for other compilers. – Fred Foo Mar 06 '13 at 15:36
  • OK, there is a builtin but setjmp is also a function, so the statement about setjmp being defined in all platforms still holds. I know I shouldn't rely on it but I really need to. – fons Mar 06 '13 at 15:38
  • 1
    @fons: the statement does not hold until you check all platforms. I've got the feeling that you're solving the wrong problem, but without details of why you need this pointer comparison from hell, I can't say what the solution would be except a load of `#ifdef`s. – Fred Foo Mar 06 '13 at 15:42
  • It's too long to explain, really. But you have helped a lot, I might have a cleaner solution now. Thanks! – fons Mar 06 '13 at 15:51
  • @fons: final remark: if all you want to do is pointer comparisons, then there is a workaround, which is to use a dummy object to stand in for `sigsetjmp`. Of course, the side that produces the function pointers should use that as well. – Fred Foo Mar 06 '13 at 15:59
  • 1
    Even built-in functions need prototypes. – ams Mar 06 '13 at 17:09
  • @ams and, in many cases (I don't know about the particular case of setjmp) using a builtin results in calling a symbol provided by the compiler library (e.g. libgcc). – fons Mar 07 '13 at 18:53
0

How about:

void my_sigsetjmp(sigjmp_buf env, int savesigs)
{
    sigsetjmp(env, savesigs);
}

Then use the address of &my_sigsetjmp

Neil
  • 11,059
  • 3
  • 31
  • 56
-1

For what you say you want, I would do something like:

#include <setjmp.h>
#ifdef sigsetjmp
#define AVOID_FUNCTION_MACRO /* expand to nothing */
int sigsetjmp AVOID_FUNCTION_MACRO (sigjmp_buf env, int savesigs) {
    log_fatal_error("Can't call sigsetjmp via pointer, its a macro!");
    abort();
}
#endif

This will give you a sigsetjmp function you can take the address of, but you can't actually call it...

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • I am not sure I follow. Wouldn't that code simply expand to `int sigsetjmp(sigjmp_buf env, int savesigs) { ... }` ? Then I could get the address of the `sigsetjmp` function we just defined, but it would be different to the address of `__sigsetjmp`, which is what I want. – fons Mar 06 '13 at 18:23
  • @fons: that is the idea. The `__sigsetjmp` function is just one specific implementation of sigsetjmp -- other macro-based solutions do something different. If sigsetjmp is a macro, you can't (in general) get a function pointer that you can call, as even if there is a function, it likely has massaged arguments (which is why the macro is needed). – Chris Dodd Mar 06 '13 at 22:45