-
Notifications
You must be signed in to change notification settings - Fork 67
Defer Macro
thinlang edited this page Apr 16, 2024
·
11 revisions
Implementation of a defer macro for library consideration.
#include <iostream>
#include <utility>
using namespace std;
struct annotate {
annotate() { cout << "annotate ctor\n"; }
annotate(const annotate&) { cout << "annotate copy-ctor\n"; }
annotate(annotate&&) noexcept { cout << "annotate move-ctor\n"; }
annotate& operator=(const annotate&) { cout << "annotate copy-assign\n"; return *this; }
annotate& operator=(annotate&&) noexcept { cout << "annotate move-assign\n"; return *this; }
~annotate() { cout << "annotate dtor\n"; }
friend inline void swap(annotate&, annotate&) { cout << "annotate swap\n"; }
friend inline bool operator==(const annotate&, const annotate&) { return true; }
friend inline bool operator!=(const annotate&, const annotate&) { return false; }
};
annotate g() {
return annotate{};
}
void f(annotate) { }
template <class F, class Require = std::enable_if_t<std::is_nothrow_invocable_v<F>>>
struct defer {
F _f;
defer(F&& f) : _f{std::move(f)} { }
~defer() { _f(); }
};
struct no_return{ };
#define STLAB_MACRO_CONCAT_(X, Y) X##Y
#define STLAB_MACRO_CONCAT(X, Y) STLAB_MACRO_CONCAT_(X, Y)
#define STLAB_ESCAPE(...) __VA_ARGS__
#define STLAB_STRIP_PARENS(X) X
#define STLAB_DEFER(F) \
defer STLAB_MACRO_CONCAT(stlab_defer_, __LINE__){[&]() noexcept -> no_return { \
{ STLAB_STRIP_PARENS(STLAB_ESCAPE F) } \
return no_return{}; \
}};
int main() {
int i{42};
STLAB_DEFER((cout << i << "\n";))
STLAB_DEFER((cout << i << "\n";))
i = 54;
cout << "End of function\n";
}
Alternative suggestion for avoiding double (())
in macro call and allows defining capture scope
(would probably benefit from the std::enable_if_t<std::is_nothrow_invocable_v<F>>>
of the above example)
enum class defer_adder {};
template <typename CB> struct defer final {
defer(CB&& _cb) : cb{ std::forward<CB>(_cb) } {}
~defer() {
try {
cb();
}
catch (...) {
}
}
private:
CB cb;
};
template <typename CB> auto operator+(defer_adder, CB&& cb) { return defer<CB>{std::forward<CB>(cb)}; }
#define DEFER_CONCAT2(X, Y) X ## Y
#define DEFER_CONCAT(X, Y) DEFER_CONCAT2(X, Y)
#define DEFER(...) const auto DEFER_CONCAT(defer_, __LINE__) = defer_adder{} + [__VA_ARGS__]() mutable
int main(int argc, char* argv[]) {
std::cout << argc << std::endl;
DEFER(&argc) {
std::cout << argc << std::endl;
--argc;
std::cout << argc << std::endl;
};
DEFER(&) {
std::cout << argc << std::endl;
argc -= 3;
std::cout << argc << std::endl;
};
std::cout << argc << std::endl;
return 0;
}
perhaps a hybrid like this?
enum class defer_adder {};
template <typename CB> struct defer final {
static_assert(std::is_nothrow_invocable_v<CB>);
static_assert(std::is_same_v<std::invoke_result_t<CB>, void>);
explicit defer(CB&& cb) : _cb{ std::forward<CB>(cb) } {}
~defer() { _cb(); }
private:
defer(const defer&) = delete;
defer& operator=(const defer&) = delete;
defer(defer&&) = delete;
defer& operator=(defer&&) = delete;
CB _cb;
};
template <typename CB> auto operator+(defer_adder, CB&& cb) { return defer<CB>{std::forward<CB>(cb)}; }
#define DEFER_CONCAT2(X, Y) X ## Y
#define DEFER_CONCAT(X, Y) DEFER_CONCAT2(X, Y)
#define DEFER(...) const auto DEFER_CONCAT(defer_, __LINE__) = defer_adder{} + [__VA_ARGS__]() mutable noexcept