Skip to content

Instantly share code, notes, and snippets.

@isilence
Last active August 4, 2018 14:02
Show Gist options
  • Save isilence/6b48b19ad0c40f493c4c44d590ac0a3b to your computer and use it in GitHub Desktop.
Save isilence/6b48b19ad0c40f493c4c44d590ac0a3b to your computer and use it in GitHub Desktop.
optimised defer for c & c++ (c++11, gcc, partially clang)
/*
* MIT License
*
* Copyright (c) 2018 Pavel Begunkov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef DEFER_H__
#define DEFER_H__
/*
* 1. defer { <statements> };
* executes specified statements at the end of the nearest block capturing
* all visible vars/symbols.
*
* 2. defer should not be used within implicit blocks (e.g. if (1) defer {...};)
* as it'd execute <statements> immediately
*
* 3. return value is evaluated before defer executed (as well as c++ d-tors)
* defer { p = NULL; };
* p = ptr;
* return p; // return ptr, not NULL
*
* 4. Example:
* void *p1 = 0, *p2 = 0;
* defer {if (p1) free(p1);};
*/
#if defined(__GNUC__)
#define defer_force_inline __attribute__((always_inline))
#elif defined(__MSVC__)
#define defer_force_inline __forceinline
#else
#define defer_force_inline
#endif
/* c++ impl requires c++11 lambdas, auto */
#if !defined(defer_impl) && defined(__cplusplus)
#include <utility>
namespace {
template <class Dtor>
class defer_guard
{
Dtor dtor;
public:
defer_force_inline
~defer_guard()
{
dtor();
}
defer_force_inline constexpr
defer_guard(Dtor&& dtor) noexcept
: dtor(std::move(dtor))
{}
};
struct defer_dummy {};
template <class Dtor>
defer_force_inline constexpr
defer_guard<Dtor> operator*(const defer_dummy, Dtor&& dtor) noexcept
{
return defer_guard<Dtor>(std::move(dtor));
}
}
/* need dummy_var to fail ```if (...) defer``` */
#define defer_impl_named(guard_var, dummy_var) \
constexpr int dummy_var = 0; \
const auto guard_var = ((void)dummy_var, defer_dummy{}) * [&]()
#define defer_impl(uid) \
defer_impl_named(defer_vguard ## uid, defer_vdummy ## uid)
#endif
/* 1. read-only capture (need __block specifier)
* 2. requires -fblocks -lBlocksRuntime
*/
#if !defined(defer_impl) && defined(__clang__)
#if __has_extension(blocks)
static defer_force_inline inline
void defer_cleanup(void (^* const block)(void))
{
(*block)();
}
#define defer_impl_named(dvar) \
__attribute__((cleanup(defer_cleanup))) void (^dvar)(void) = ^
#define defer_impl(uid) defer_impl_named(defer_dvar ## uid)
#endif
#endif
/* plain C version (gcc only).
* Requires nested functions and cleanup exts
*/
#if !defined(defer_impl) && !defined(__clang__)
#define defer_impl_named(dtor, dvar) \
auto void dtor(); \
const int __attribute__((cleanup( dtor ))) dvar \
__attribute__((unused)) = 0; \
\
defer_force_inline inline \
void dtor(const int * const restrict def_param __attribute__((unused)))
#define defer_impl(uid) \
defer_impl_named(defer_dtor ## uid, defer_dvar ## uid)
#endif
#if defined(defer_impl)
#define HAS_DEFER 1
#define defer_counted_resolve_(uid) defer_impl(uid)
#define defer defer_counted_resolve_(__COUNTER__)
#elif !defined(DEFER_NOT_REQUIRED)
#error "defer not supported"
#endif
#endif /* DEFER_H__ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment