Emulating Swift's “defer” in C, with Clang or GCC+Blocks
fdiv.net
fdiv.net
C++ templates are Turing-complete. You can do arbitrary computation at compile time. Once this was discovered, ever-fancier templates became a basic part of C++. (Take a look at the huge templates that implement "max" and "min".) Then the C++ standards committee went off into template la-la land, focusing on adding features to make the template system more powerful. After a decade of this, many standard templates are now at the "you are not supposed to understand this" level.
This has scared people off of C++, which is why there's still much work in standard C. So now we're seeing features added to C to solve specific problems, but without an architecture.
"Defer" is troublesome, because dealing with errors in deferred statements is hard. C++ has the same problem with destructors. A "with" clause, which LISP introduce as "with-open-file" and Python also supports, is a better way to do this. Python's combination of "with" and exception handling can handle an error when closing out a resource, and get all the nested resources closed out properly. Few other languages even try to get that right.
// min
template <class _Tp, class _Compare>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
min(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__b, __a) ? __b : __a;
}
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
min(const _Tp& __a, const _Tp& __b)
{
return _VSTD::min(__a, __b, __less<_Tp>());
}
#ifndef _LIBCPP_HAS_NO_GENERALIZED_INITIALIZERS
template<class _Tp, class _Compare>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
_Tp
min(initializer_list<_Tp> __t, _Compare __comp)
{
return *__min_element(__t.begin(), __t.end(), __comp);
}
template<class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
_Tp
min(initializer_list<_Tp> __t)
{
return *__min_element(__t.begin(), __t.end(), __less<_Tp>());
}
#endif // _LIBCPP_HAS_NO_GENERALIZED_INITIALIZERS #define min(x,y) ((x) < (y) ? (x) : (y)) #define min(X, Y) \
({ typeof (X) x_ = (X); \
typeof (Y) y_ = (Y); \
(x_ < y_) ? x_ : y_; })(Note that, IMO, the semantics of the feature as implemented in the article are preferable to those of Golang, so I wouldn't personally go to the effort of trying to duplicate Go's behavior.)
Instead, I'd suggest just making regular use of `__attribute__((cleanup))` in C under GCC or Clang and avoiding C blocks entirely. In this case you could do something like:
#define AUTOCLOSE_FILE(n) __attribute__((cleanup(autoclose_file))) n = NULL
void
autoclose_file(FILE *f)
{
if (f)
fclose(f);
}
int
main(...)
{
FILE AUTOCLOSE_FILE(*a), AUTOCLOSE_FILE(*b);
a = fopen("foo");
if (!a)
return;
b = fopen("bar");
if (!b)
return;
...
}I wasn't aware of C Blocks.
int main(){ __block int (^foo)(int x) = ^ int (int x) { if(x<100) return foo(x+10); return x; }; return foo(69); }
versus the GCC: int main(){ int foo(int x) { if(x<100) return foo(x+10); return x; }; return foo(69); }
if you want to see what I'm talking about.CLANG: http://pastebin.com/37A9by4V
GCC: http://pastebin.com/RMEDnwxi
However, defer does look interesting, and implementing it for GCC is very easy:
#define defer_(x) do{}while(0); \
auto void _dtor1_##x(); \
auto void _dtor2_##x(); \
int __attribute__((cleanup(_dtor2_##x))) _dtorV_##x=69; \
void _dtor2_##x(){if(_dtorV_##x==42)return _dtor1_##x();};_dtorV_##x=42; \
void _dtor1_##x()
#define defer__(x) defer_(x)
#define defer defer__(__COUNTER__)
You don't have to use the stupid block-syntax either, just: in = fopen("whatever", "r");
defer { fclose(in); } if (cond)
MACRO;
and the macro won't expand incorrectly to if (cond)
foo;
bar;
I personally just go the anal retentive route and use brackets everywhere.if(x){defer} will be wrong, but it'll be wrong in a different way.
Furthermore, it's fun to play with this, but it is undeniably not c. If you have access to clang everywhere (or gcc w/ blocks) and you're OK with the tradeoffs, have at it. :)