Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Casting jmp_buf to void *

6 views
Skip to first unread message

Noob

unread,
Mar 11, 2010, 12:10:52 PM3/11/10
to
Hello everyone,

I'm using a library which provides a void *user_data field
inside the struct used everywhere within the library.

I need to stuff a jmp_buf inside user_data.

I suppose I can't write

jmp_buf env;
ctx->user_data = env;

and then use ctx->user_data as setjmp's and longjmp's parameter?

Instead, I'd have to write

jmp_buf env;
ctx->user_data = &env;

and when I need the jmp_buf, I have to write
*(jmp_buf *)ctx->user_data
or
jmp_buf *envp = ctx->user_data;
*envp

Did I get it right?
Is there different way to do this?

Regards.

Eric Sosman

unread,
Mar 11, 2010, 1:26:16 PM3/11/10
to
On 3/11/2010 12:10 PM, Noob wrote:
> Hello everyone,
>
> I'm using a library which provides a void *user_data field
> inside the struct used everywhere within the library.
>
> I need to stuff a jmp_buf inside user_data.
>
> I suppose I can't write
>
> jmp_buf env;
> ctx->user_data = env;
>
> and then use ctx->user_data as setjmp's and longjmp's parameter?

Yes, you can. For historical reasons jmp_buf is an array
type, and you know The Rule about arrays: In all but a few
contexts, mentioning the name of an array is the same as writing
a pointer to the array's first element. So

ctx->user_data = env;

is the same as

ctx->user_data = &env[0];

> Instead, I'd have to write
>
> jmp_buf env;
> ctx->user_data = &env;

This would also work, but there's no pressing need to
write it this way.

> and when I need the jmp_buf, I have to write
> *(jmp_buf *)ctx->user_data
> or
> jmp_buf *envp = ctx->user_data;
> *envp

Since the only (useful) thing you can do with a pointer
to the first element of a jmp_buf is call longjmp() with it,
and since longjmp() is an ordinary function call, a void*
argument will automatically convert to the proper type as
part of the call.

The situation with setjmp() is less clear. Since setjmp()
is a macro rather than a function (although its expansion may
call one or more functions), it might do things with its jmp_buf
argument that an ordinary function could not do via a pointer.
As far as I can tell, calling setjmp() with a pointer to the
start of a jmp_buf is *not* guaranteed to work.

> Did I get it right?

Sort of, I guess.

> Is there different way to do this?

It's not clear what you mean by "this." You can certainly
call setjmp() on a jmp_buf object, pass around a void* pointer
to that object, and eventually call longjmp() on the pointer.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Ben Bacarisse

unread,
Mar 11, 2010, 1:45:29 PM3/11/10
to
Noob <ro...@127.0.0.1> writes:

> I'm using a library which provides a void *user_data field
> inside the struct used everywhere within the library.
>
> I need to stuff a jmp_buf inside user_data.
>
> I suppose I can't write
>
> jmp_buf env;
> ctx->user_data = env;
>
> and then use ctx->user_data as setjmp's and longjmp's parameter?

In fact you can do this (but see below for some problems) because a
jmp_buf is an array type.

> Instead, I'd have to write
>
> jmp_buf env;
> ctx->user_data = &env;

This is pretty much the same as the above. The main trouble is going
to be the lifetime of 'env'. All will be well provided 'env' still
exists when the point to it is used, but if the function containing
'env' might return before the pointer is used, you'll have to use
malloc to allocate storage for the jmp_buf.

> and when I need the jmp_buf, I have to write
> *(jmp_buf *)ctx->user_data
> or
> jmp_buf *envp = ctx->user_data;
> *envp

You need to do this no matter how you get the pointer into user_data
because once it is a void * you can't do much with it.

Because jmp_buf is an array type, you might think that you can avoid
the cast and the dereference because the result of

*(jmp_buf *)ctx->user_data

is an array lvalue and thus gets converted to a pointer anyway.
Surely (you might think) the void * expression ctx->user_data is just
as good?

The problem with that is that setjmp may be implemented as a macro, so
you can't be sure that it does not use some construct (sizeof and &
being the most obvious) that distinguishes between an array and a
pointer to its first element.

longjmp, however, is a function so I think it is safe to use

longjmp(ctx->user_data, value);

> Did I get it right?

Pretty much so, yes. I wonder if my answer fairs as well :-)

<snip>
--
Ben.

Peter Nilsson

unread,
Mar 11, 2010, 5:12:47 PM3/11/10
to
Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
> ... The situation with setjmp() is less clear.  Since

> setjmp() is a macro rather than a function (although its
> expansion may call one or more functions), it might do
> things with its jmp_buf argument that an ordinary function
> could not do via a pointer. As far as I can tell, calling
> setjmp() with a pointer to the start of a jmp_buf is *not*
> guaranteed to work.

There is nevertheless an implicit prototype for the macro.
Almost any function in the standard library may be implemented
as a macro.

Is pow(2, 0.5) undefined since pow may be implemented as a
macro and 2 is not of type double? What about atan2(1, 1),
time(0), fgets(line, sizeof line, stdin), strtol(s, 0, 10),
tolower((unsigned char) ch), ...

--
Peter

Keith Thompson

unread,
Mar 11, 2010, 5:32:12 PM3/11/10
to

The standard doesn't explicitly address the question of implicit
conversions of macro arguments, but C99 7.1.4 does seem to imply
(or at least assume) that it's guaranteed to work correctly.

A macro implementation of pow() could just cast its arguments
to double.

There is one potential problem: a macro invocation might not contain
the same sequence points that a function call does. So, for example,
the behavior of ``sin(x) + cos(x)'' might be undefined because both
calls potentially update errno.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Phil Carmody

unread,
Mar 11, 2010, 7:48:05 PM3/11/10
to
Peter Nilsson <ai...@acay.com.au> writes:
> Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
>> ... The situation with setjmp() is less clear.  Since
>> setjmp() is a macro rather than a function (although its
>> expansion may call one or more functions), it might do
>> things with its jmp_buf argument that an ordinary function
>> could not do via a pointer. As far as I can tell, calling
>> setjmp() with a pointer to the start of a jmp_buf is *not*
>> guaranteed to work.
>
> There is nevertheless an implicit prototype for the macro.

What do you mean by "prototype"?

> Almost any function in the standard library may be implemented
> as a macro.

Without serially repeating, and thus evaluating, their arguments
more than once, I'd be surprised if more than a handful could be
so implemented. And the ones that do evaluate any argument more
than once cannot be valid implementations.

> Is pow(2, 0.5) undefined since pow may be implemented as a
> macro and 2 is not of type double?

In what context is the value 2 not convertable to a required double?
If pow() starts taking addresses, then you're screwed, but that
would be a strawman soaked in parafin.

Phil
--
I find the easiest thing to do is to k/f myself and just troll away
-- David Melville on r.a.s.f1

pete

unread,
Mar 11, 2010, 8:03:04 PM3/11/10
to
Peter Nilsson wrote:

> Is pow(2, 0.5) undefined since pow may be implemented as a
> macro and 2 is not of type double?

I don't think so.
To me, implementing a function as a macro,
means that the macro must do what the function is supposed to do.

--
pete

Michael Foukarakis

unread,
Mar 12, 2010, 4:13:04 AM3/12/10
to
On Mar 11, 7:10 pm, Noob <r...@127.0.0.1> wrote:
> Hello everyone,
>
> I'm using a library which provides a void *user_data field
> inside the struct used everywhere within the library.
>
> I need to stuff a jmp_buf inside user_data.
>
> I suppose I can't write
>
>    jmp_buf env;
>    ctx->user_data = env;
>
> and then use ctx->user_data as setjmp's and longjmp's parameter?

As long as you make sure the pointer won't dangle, you can do this.

Noob

unread,
Mar 12, 2010, 7:33:08 AM3/12/10
to
Eric Sosman wrote:

> Noob wrote:
>
>> I'm using a library which provides a void *user_data field
>> inside the struct used everywhere within the library.
>>
>> I need to stuff a jmp_buf inside user_data.
>>
>> I suppose I can't write
>>
>> jmp_buf env;
>> ctx->user_data = env;
>>
>> and then use ctx->user_data as setjmp's and longjmp's parameter?
>
> Yes, you can. For historical reasons jmp_buf is an array type,

I was not aware of that. Thank you for pointing it out to me.

<quote C89 draft>

The type declared is

jmp_buf

which is an array type suitable for holding the information needed to
restore a calling environment.

</quote>

> and you know The Rule about arrays: In all but a few
> contexts, mentioning the name of an array is the same as writing
> a pointer to the array's first element. So
>
> ctx->user_data = env;
>
> is the same as
>
> ctx->user_data = &env[0];

On a moderately related note, I've been wondering...

Given int foo[42];

&foo[0] and &foo will not be compatible pointers, right?

Yet, would the two pointers have the same value?
i.e. (uintptr_t)&foo[0] ==(uintptr_t)&foo ???

>> Instead, I'd have to write
>>
>> jmp_buf env;
>> ctx->user_data = &env;
>
> This would also work, but there's no pressing need to
> write it this way.

Cool.

> Since the only (useful) thing you can do with a pointer
> to the first element of a jmp_buf is call longjmp() with it,
> and since longjmp() is an ordinary function call, a void*
> argument will automatically convert to the proper type as
> part of the call.
>
> The situation with setjmp() is less clear. Since setjmp()
> is a macro rather than a function (although its expansion may
> call one or more functions), it might do things with its jmp_buf
> argument that an ordinary function could not do via a pointer.
> As far as I can tell, calling setjmp() with a pointer to the
> start of a jmp_buf is *not* guaranteed to work.
>
>> Did I get it right?
>
> Sort of, I guess.
>
>> Is there different way to do this?
>
> It's not clear what you mean by "this." You can certainly
> call setjmp() on a jmp_buf object, pass around a void* pointer
> to that object, and eventually call longjmp() on the pointer.

What you describe is precisely what I did, based on your advice.

static void myexit(j_common_ptr cinfo)
{
longjmp(cinfo->client_data, 666);
}

static int decode_image(char *file, int *w, int *h)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
jmp_buf jmpbuf;
int err = 0;
cinfo.err = jpeg_std_error(&jerr);
cinfo.client_data = jmpbuf;
jerr.error_exit = myexit;
if (setjmp(jmpbuf) != 0)
{
err = FAIL;
goto unwind;
}
jpeg_create_decompress(&cinfo);
...
unwind:
jpeg_destroy_decompress(&cinfo);
vfs_fclose(infile);
return err;
}

(jerr.error_exit is called from within the library whenever a fatal
condition occurs, and the current operation cannot proceed.)

Regards.

Noob

unread,
Mar 12, 2010, 7:36:41 AM3/12/10
to
Ben Bacarisse wrote: [snip]

Thanks to you, and to Eric, for your detailed and insightful answers.

Eric Sosman

unread,
Mar 12, 2010, 8:57:05 AM3/12/10
to

The problem here is the opposite one: setjmp() *is* a
macro, not a function that may also be masked by a macro.
On some platforms the setjmp() macro may simply expand to
a call on a setjmp() function, and the Standard reserves
that name for external linkage, but that's not required.
The possibility remains open that the setjmp() macro might
expand to some code that works only with an actual jmp_buf
array and not with a pointer thereto. Something like

#define setjmp(buf) ( \
(buf)[0]._secret_ = sizeof(buf) , \
(buf)[0]._arcane_ = &(buf) , \
setjmp(buf) \
)

would behave differently with a pointer than with an actual
jmp_buf.

The Rationale discusses some of the reasons why "setjmp()
should be usable as an ordinary function" is deliberately
not required by the Standard.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Eric Sosman

unread,
Mar 12, 2010, 9:07:22 AM3/12/10
to
On 3/12/2010 7:33 AM, Noob wrote:
>[...]

> On a moderately related note, I've been wondering...
>
> Given int foo[42];
>
> &foo[0] and &foo will not be compatible pointers, right?

See Question 6.12 in the FAQ (http://www.c-faq.com/).

--
Eric Sosman
eso...@ieee-dot-org.invalid

Peter Nilsson

unread,
Mar 14, 2010, 6:52:39 PM3/14/10
to
Cross posted to comp.std.c.

Indeed, setjmp is a pure macro and needn't be a function,
but there is a (notional) explicit prototype, unlike assert
whose argument is (now) simply <scalar type>.

However, thinking about it, I can't find any chapter and
verse that says a macro implementation of a standard
function must convert its arguments to the appropriate
type. The only real guarantees are...

"... Any invocation of a library function that is
implemented as a macro shall expand to code that
evaluates each of its arguments exactly once, fully
protected by parentheses where necessary, so it is
generally safe to use arbitrary expressions as
arguments. Likewise, those function-like macros
described in the following subclauses may be invoked
in an expression anywhere a function with a
compatible return type could be called. ..."

'Protected by parentheses' does not imply an appropriate
conversion where desirable.

So, not uncommon constructs remain potential UB [e.g
atan2(1,1), time(0), fgets(line, sizeof line, stdin),
strtol(s, 0, 10), tolower((unsigned char) ch), ...]

I think that's a bug in the standard.

--
Peter

Nobody

unread,
Mar 17, 2010, 10:02:08 AM3/17/10
to
On Thu, 11 Mar 2010 18:45:29 +0000, Ben Bacarisse wrote:

>> Instead, I'd have to write
>>
>> jmp_buf env;
>> ctx->user_data = &env;
>
> This is pretty much the same as the above. The main trouble is going
> to be the lifetime of 'env'. All will be well provided 'env' still
> exists when the point to it is used, but if the function containing
> 'env' might return before the pointer is used, you'll have to use
> malloc to allocate storage for the jmp_buf.

Using malloc() isn't going to help. Once you leave the function in which
setjmp() was called, trying to longjmp() back to that point isn't likely
to work. So you may as well use an automatic variable.

Ben Bacarisse

unread,
Mar 17, 2010, 11:20:22 AM3/17/10
to
Nobody <nob...@nowhere.com> writes:

Good point. I was blindly giving general advice without thinking
about the particulars of the case.

--
Ben.

Tim Rentsch

unread,
Mar 23, 2010, 10:16:39 AM3/23/10
to
Noob <ro...@127.0.0.1> writes:

> Hello everyone,
>
> I'm using a library which provides a void *user_data field
> inside the struct used everywhere within the library.
>
> I need to stuff a jmp_buf inside user_data.
>
> I suppose I can't write
>
> jmp_buf env;
> ctx->user_data = env;
>
> and then use ctx->user_data as setjmp's and longjmp's parameter?

Right, doing that is needlessly dangerous.


> Instead, I'd have to write
>
> jmp_buf env;
> ctx->user_data = &env;
>
> and when I need the jmp_buf, I have to write
> *(jmp_buf *)ctx->user_data
> or
> jmp_buf *envp = ctx->user_data;
> *envp
>
> Did I get it right?

Yes, this should work fine, and is better in terms of coding
style too. Notice that using the '&' as you have here,
and casting to a pointer type upon converting back, means this
technique will work whether or not jmp_buf is an array type.

It happens that the Standard requires that jmp_buf be an array
type, but it's better to write code that doesn't depend on that
requirement if that's not too hard (and here it isn't).


> Is there different way to do this?

The way you explain in the second part is perfectly fine.

Eric Sosman

unread,
Mar 23, 2010, 10:28:00 AM3/23/10
to
On 3/23/2010 10:16 AM, Tim Rentsch wrote:
>
> [...] Notice that using the '&' as you have here,

> and casting to a pointer type upon converting back, means this
> technique will work whether or not jmp_buf is an array type.

7.13p2: "The type declared is jmp_buf which is an array
type [...]"

--
Eric Sosman
eso...@ieee-dot-org.invalid

Tim Rentsch

unread,
Mar 23, 2010, 10:36:50 AM3/23/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 3/11/2010 12:10 PM, Noob wrote:
>> Hello everyone,
>>
>> I'm using a library which provides a void *user_data field
>> inside the struct used everywhere within the library.
>>
>> I need to stuff a jmp_buf inside user_data.
>>
>> I suppose I can't write
>>
>> jmp_buf env;
>> ctx->user_data = env;
>>
>> and then use ctx->user_data as setjmp's and longjmp's parameter?
>
> Yes, you can. For historical reasons jmp_buf is an array
> type, and you know The Rule about arrays: In all but a few
> contexts, mentioning the name of an array is the same as writing
> a pointer to the array's first element. So
>
> ctx->user_data = env;
>
> is the same as
>
> ctx->user_data = &env[0];
>
>> Instead, I'd have to write
>>
>> jmp_buf env;
>> ctx->user_data = &env;
>
> This would also work, but there's no pressing need to
> write it this way.

I'm surprised to see this comment from you. Using the '&' is
clearly better style -- the reader doesn't have to know that
'jmp_buf' is an array type, and this code works whether
it's an array type or not.


>> and when I need the jmp_buf, I have to write
>> *(jmp_buf *)ctx->user_data
>> or
>> jmp_buf *envp = ctx->user_data;
>> *envp
>
> Since the only (useful) thing you can do with a pointer
> to the first element of a jmp_buf is call longjmp() with it,
> and since longjmp() is an ordinary function call, a void*
> argument will automatically convert to the proper type as
> part of the call.
>
> The situation with setjmp() is less clear. Since setjmp()
> is a macro rather than a function (although its expansion may
> call one or more functions), it might do things with its jmp_buf
> argument that an ordinary function could not do via a pointer.
> As far as I can tell, calling setjmp() with a pointer to the
> start of a jmp_buf is *not* guaranteed to work.

I read the Standard as saying setjmp() has to work with any
expression (without side effects) that has type 'jmp_buf',
but there are no guarantees if called with an expression
that has type (void*) or the type of '&env[0]'. Is that
the same as your reading, or different?

Of course calling longjmp() treats its argument as any other
function call would, but there is no reason not to use
exactly the same expression in the longjmp() call as was
used in the setjmp() call.


>> Did I get it right?
>
> Sort of, I guess.
>
>> Is there different way to do this?
>
> It's not clear what you mean by "this." You can certainly
> call setjmp() on a jmp_buf object, pass around a void* pointer
> to that object, and eventually call longjmp() on the pointer.

Certainly the Standard means to allow that setjmp() can be
called with any expression (again, without side-effects) of
type 'jmp_buf', don't you think?

Tim Rentsch

unread,
Mar 23, 2010, 10:52:03 AM3/23/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 3/11/2010 8:03 PM, pete wrote:
>> Peter Nilsson wrote:
>>
>>> Is pow(2, 0.5) undefined since pow may be implemented as a
>>> macro and 2 is not of type double?
>>
>> I don't think so.
>> To me, implementing a function as a macro,
>> means that the macro must do what the function is supposed to do.
>
> The problem here is the opposite one: setjmp() *is* a
> macro, not a function that may also be masked by a macro.
> On some platforms the setjmp() macro may simply expand to
> a call on a setjmp() function, and the Standard reserves
> that name for external linkage, but that's not required.
> The possibility remains open that the setjmp() macro might
> expand to some code that works only with an actual jmp_buf
> array and not with a pointer thereto. Something like
>
> #define setjmp(buf) ( \
> (buf)[0]._secret_ = sizeof(buf) , \
> (buf)[0]._arcane_ = &(buf) , \
> setjmp(buf) \
> )
>
> would behave differently with a pointer than with an actual
> jmp_buf.

Using these declarations

jmp_buf env;
jmp_buf several[10];
jmp_buf *x = &several[2];

the above macro would work just fine with any of

env
several[3]
*(several + 7)
*x

as arguments, would it not?


> The Rationale discusses some of the reasons why "setjmp()
> should be usable as an ordinary function" is deliberately
> not required by the Standard.

It does, but that discussion seems largely orthogonal to the
question of what kinds of expressions are acceptable as its
argument.

Tim Rentsch

unread,
Mar 24, 2010, 7:31:04 PM3/24/10
to
Eric Sosman <eso...@ieee-dot-org.invalid> writes:

> On 3/23/2010 10:16 AM, Tim Rentsch wrote:
>>
>> [...] Notice that using the '&' as you have here,
>> and casting to a pointer type upon converting back, means this
>> technique will work whether or not jmp_buf is an array type.
>
> 7.13p2: "The type declared is jmp_buf which is an array
> type [...]"

Yes I know that. My point is that it's better as a general rule
not to write code that depends on that if we don't have to, and
we don't have to. And it's easier on the reader -- not everyone
knows the C Standard to the level of detail that clc'ers
generally do. And, what I think is most significant, the later
use of said pointer value being converted to type 'jmp_buf *',
using '&env' rather than just plain 'env' will cause less head
scratching in general -- even people who _know_ that jmp_buf is
an array type might take a moment or two to remember that. There
are plenty of things to think about when reading code more
important than this; when I write code I don't want readers of
that code to spend even one second thinking about stuff they
don't have to.

0 new messages