Defer
Defer copied to clipboard
Make it more portable?
goto *_deferrals[--_num_deferrals]
is not portable C :(
There is good news though: you could use a Duff's device so that each deferral is a switch case and then you can while (_num_deferrals)
loop around the switch. Not unlike async.h or Simon Tatham's C co-routines.
There is a portable version using setjmp/longjmp (look at the second half of the file); unfortunately, because longjmp tries to be clever, it causes problems when optimizations are enabled, and with dynamic stack allocations.
I did attempt a switch-based version, but was unsuccessful.
Leaving this open as a tracking issue for portability, though.
The other problem with a switch/case-based version is that you need to have another macro to put a closing brace at the end of the function. Which is not a huge deal, but it still adds friction and makes the library less 'natural' to use.
Yeah, I thought of the closing brace, but I'm ok with that. I can't use this with the setjmp()
/longjmp()
approach, and I can't use GCC/clang extensions. I'm also not sure I'd use it if it used a Duff's device either, but I am sure I need this :)
Oh, I think I see the problem with a Duff's device...
EDIT: Nah, made it work! PR submitted! Feel free to reject, naturally. And, yes, sadly, I did need BeginDeferral
/EndDeferral
macros :(
Can you elaborate on why you can't use the gcc extensions? Using MSVC? Or on an embedded platform?
This can be implemented safely using relatively trivial assembly routines; if that would suffice, let me know the platform and I can whip something up.
Yeah, MSVC :(
So if I make a version which uses amd64 assembly (masm syntax, I guess?), would that work for you?
Don't waste your time on that yet. If I could use this, I'd use it in Heimdal), but that would require consensus among us maintainers. I did the Duff's device thing for the fun of it. Thanks!
I've figured out how to make it work with MSVC using ASM. Essentially we need two macros, TakeLabelAddress(destination, label_name)
and GotoLabelAddress(addr)
that look like this:
#define TakeLabelAddress(dest, l) \
do { void *_label; __asm{ mov [_label],offset l }; dest = _label; } while(0)
#define GotoLabelAddress(a) do { void *_label = (a); __asm{ jmp _label } } while (0)
A simplified defer.h
for just MSVC (i.e., w/o #ifdef
spaghetti) looks like this:
#ifndef DEFER_H
#define DEFER_H
#ifndef DEFER_MAX_DEFERRED_STATEMENTS
# define DEFER_MAX_DEFERRED_STATEMENTS 32
#endif
#define TakeLabelAddress(dest, l) \
do { void *_labelv; __asm{ mov [_labelv],offset l }; dest = _labelv; } while(0)
#define GotoLabelAddress(a) do { _label = (a); __asm{ jmp _label } } while (0)
#define Deferral \
unsigned char _num_deferrals = 0; \
void *_defer_return_loc = 0, *_deferrals[DEFER_MAX_DEFERRED_STATEMENTS] = {0};
# define Defer(block) _Defer(block, __COUNTER__)
# define Return _Return(__COUNTER__)
#define _defer_tokpaste(a, b) a ## b
#define _Defer(block, n) do { \
TakeLabelAddress(_deferrals[_num_deferrals++], _defer_tokpaste(_defer_ini, n)); \
if (0) { \
_defer_tokpaste(_defer_ini, n): \
block; \
if (_num_deferrals) { \
GotoLabelAddress(_deferrals[--_num_deferrals]); \
} else { \
GotoLabelAddress(_defer_return_loc); \
} \
} \
} while (0)
#define _Return(n) \
if (_num_deferrals) { \
TakeLabelAddress(_defer_return_loc, _defer_tokpaste(_defer_fini_, n)); \
GotoLabelAddress(_deferrals[--_num_deferrals]); \
} \
\
_defer_tokpaste(_defer_fini_, n): \
return
#endif /*DEFER_H*/
Yes, I actually made pretty much that, when I was testing initial versions. But I canned it because __asm
only works with 32-bit msvc, and I don't really care about 32-bit x86.
It doesn't work for 64-bit??
Ugh, you're right.