Commit 1d153a45 authored by Kirill Smelkov's avatar Kirill Smelkov

libgolang: Expose defer as public C++ API

Libgolang, since 3b241983 (Port/move channels to C/C++/Pyx), already had
defer macro implemented and used, but only internally. The reason it was
not yet exposed as public API is that there is a difference with Go's
defer in that deferred function is called at end of current scope
instead of end of current function, and I was a bit reluctant to expose
defer with different-than-Go semantic.

However even with this difference defer is useful, and the difference
can be documented. Unfortunately it is not easy to correctly fix the
difference, so the most practical way for now is to expose defer as it is.

I've also contemplated how to avoid using macro, but without a macro,
users will have to explicitly declare placeholder variable for every
defer call which goes against usability.

Since defer is exposed as macro, I've also contemplated to expose it as
something like `libgolang_defer` with the idea to avoid name conflicts,
and so that users - that are using defer - will be doing `#define defer
libgolang_defer`. However I ended up not doing that and exposing `defer`
macro with its own name. My rationale is:

- grepping /usr/include/ for \<defer\> on my system did not showed any
  real usage.
- Qt also #defines `slots` and `signals` and that does not cause
  problems in practice.

-> expose `defer` macro into public C++ API as is, so that it can be
used not only inside libgolang.cpp . For example I myself need defer
functionality in C++ part of wendelin.core.
parent 9e6ff8bd
......@@ -177,6 +177,7 @@ cdef extern from * nogil:
extern void _test_close_wakeup_all_vsselect();
extern void _test_select_win_while_queue();
extern void _test_select_inplace();
extern void _test_defer();
"""
void _test_chan_cpp_refcount() except +topyexc
void _test_chan_cpp() except +topyexc
......@@ -186,6 +187,7 @@ cdef extern from * nogil:
void _test_close_wakeup_all_vsselect() except +topyexc
void _test_select_win_while_queue() except +topyexc
void _test_select_inplace() except +topyexc
void _test_defer() except +topyexc
def test_chan_cpp_refcount():
with nogil:
_test_chan_cpp_refcount()
......@@ -210,6 +212,9 @@ def test_select_win_while_queue():
def test_select_inplace():
with nogil:
_test_select_inplace()
def test_defer():
with nogil:
_test_defer()
# helpers for pychan(dtype=X) py <-> c tests.
......
......@@ -449,6 +449,20 @@ int select(const _selcase (&casev)[N]) {
return _chanselect(&casev[0], N);
}
// defer(f) mimics `defer f()` from golang.
// NOTE contrary to Go f is called at end of current scope, not function.
#define defer(f) golang::_deferred _defer_ ## __COUNTER__ (f)
struct _deferred {
typedef std::function<void(void)> F;
F f;
_deferred(F f) : f(f) {}
~_deferred() { f(); }
private:
_deferred(const _deferred&); // don't copy
_deferred(_deferred&&); // don't move
};
// golang::time::
namespace time {
......
......@@ -207,20 +207,6 @@ struct _with_lock_guard {
};
// defer(f) mimics defer from golang.
// XXX f is called at end of current scope, not function.
#define defer(f) _deferred _defer_ ## __COUNTER__ (f)
struct _deferred {
typedef std::function<void(void)> F;
F f;
_deferred(F f) : f(f) {}
~_deferred() { f(); }
private:
_deferred(const _deferred&); // don't copy
_deferred(_deferred&&); // don't move
};
// ---- channels -----
struct _WaitGroup;
......
......@@ -461,3 +461,17 @@ void _test_select_inplace() {
ASSERT(err != NULL);
ASSERT(!strcmp(err, "_selcase: prx: recv with inplace data"));
}
// verify that defer works.
void __test_defer(bool *pcalled) {
defer([&]() {
*pcalled = true;
});
return;
}
void _test_defer() {
bool called = false;
__test_defer(&called);
ASSERT(called);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment