GOPHERSPACE.DE - P H O X Y
gophering on hngopher.com
HN Gopher Feed (2017-10-21) - page 1 of 10
 
___________________________________________________________________
Implementing Go's defer keyword in C++
31 points by ingve
https://oded.blog/2017/10/05/go-defer-in-cpp/
___________________________________________________________________
 
albinofrenchy - 2 hours ago
This seems unnecessary and possibly even harmful.In C++ the
destructor handles this as part of the lifespan of the object.
Making users of the object encapsulating the resource handle
resource cleanup is an antipattern, which is why the standard
library does this for you.
 
  Occivink - 1 hours ago
  There's plenty of one-time cases where you don't want to declare
  an entire class but still enjoy scope-based functions. However,
  the approach in the post is not very C++-esque, I like this one
  much more:
  https://github.com/mawww/kakoune/blob/master/src/utils.hh#L5...
 
  alexgartrell - 1 hours ago
  ScopeGuards as imagined here (Facebook's folly library has one)
  are near-essential for writing reasonable systems code in C++.
  int fd = socket(...);    if (fd < 0) return false;    SCOPE_EXIT
  { ::close(fd); };     if (ioctl(fd, ...) != 0) return false;
  if (ioctl(fd, ...) != 0) return false;    ...     return true;
  You could totally just write the FdWrapper class, but then you're
  still a user handling resource cleanup, but with more lines.
 
    ot - 14 minutes ago
    > You could totally just write the FdWrapper classWhich, BTW,
    is exactly what folly::File does :)
 
tcbawo - 1 hours ago
It seems that this might be for only limited cases.  I'm not that
familiar with Go, but I suspect that due to coroutines, the
deferral may not execute for quite some time, while in C++ this
could be limited to the single-threaded flow of the calling method
(unless you manually move the scoped object out of the call).  To
match Go's behavior, you would need to attach the object lifetime
to whatever future/asynch mechanism you were using.  One very nice
implementation (if heavyweight) is Seastar's then chaining:
http://docs.seastar-project.org/master/md_doc_tutorial.html
 
  lucio - 1 hours ago
  go's "defer" is like java/net "finally" for a try/catch enclosing
  the entire function.pro: you can push more than one "defer" in
  the same function.con: "finally" puts the cleanup code at the end
  of the function. With "defer" you have it scattered at the
  beginning.
 
    nothrabannosir - 1 hours ago
    con: "finally" puts the cleanup code at the end of the
    function. With "defer" you have it scattered at the
    beginning.Quite commonly an advantage, e.g.:    f, err :=
    openFile('...')     if err != nil {         ...     }     defer
    f.Close()  is how it's commonly used. This makes sense because
    the cleanup lives next to the initialization of the actual
    thing it's cleaning up. In a way it becomes part of the
    initialization, itself.Same with locks and release. Now you
    don't have to scan across the entire function body to make sure
    your inits and cleanups match up.
 
    karmakaze - 1 hours ago
    > con: "finally" puts the cleanup code at the end of the
    function. With "defer" you have it scattered at the beginning.I
    would consider this a 'pro' as each action and defer are
    paired, executes in reverse order and a conditional
    action/defer doesn't need a second matching check.
 
[deleted]
 
maxpert - 55 minutes ago
Great implement a coroutine, channels, and async io using those
coroutines... WOLLAH we have a golang replacement.
 
  missblit - 44 minutes ago
  Boost asio has async IO that uses coroutines. I only ever used it
  for toy code, but it seemed to work pretty well.
 
markbnj - 1 hours ago
It's a nit I guess but I feel like RAII is a pattern, not a feature
of the language. Haven't done C++ in years but as I recall it
doesn't have anything like python's with or finally blocks, which
would both serve the purpose illustrated here. Using destructors to
perform things when exiting a block is fine, I guess, but it does
introduce hidden behaviors that will need to be well documented.
 
  stabbles - 56 minutes ago
  It's a known trick also used in the standard library of C++. For
  instance in multithreading    {     std::lock_guard
  lock(some_mutex);      ...     }  owns a mutex for duration of
  the scope. The constructor claims it, and the destructor releases
  it.But yeah, it has downsides. If you would not name the
  variable, and do `std::lock_guard(some_mutex);`, then
  its scope is limited to a single line, and the lock is
  immediately released.
 
Animats - 1 hours ago
The best solution to this problem seems to be Python's "with".
with open(oldfile, 'r', encoding='utf-8') as infile:         with
open(newfile, 'w') as outfile:             ....  The implied close
will be executed on exiting the "with". "with" calls .__enter__ on
the "open" object at entrance, and .__exit__ at exit.   This works
even if exit is via return or an exception.If the implied close
generates an exception, everything still works reasonably. __exit__
has a "traceback" parameter, which indicates an exception is in
progress.  An exit operation should do its closeout, then re-raise
the exception sent as the traceback, if any. This works even when a
destructor raises an exception. (A typical case is closing a
network connection.  If the other end dies, close will generate an
error, which should not be lost.) The default use of "with" in
Python thus comes with correct single and nested error
handling.That's not true of RAII or Go's "defer". In C++'s
destructors, you can't return an error code, and an exception in a
destructor is usually a big problem.It's hard, but not impossible,
for a deferred call in Go to report an error. The deferred function
can muck with the parameters of the function which called it. This
only works if the deferred function was written specifically for
the context in which it was called; just deferring a "close" won't
do this. So you don't get proper error handling by
default.(Languages seem to need exceptions. Both Go and Rust
started with an always-aborts panic mechanism. That was too drastic
in practice, and in both languages, panicking was made recoverable
and provided with unwinding machinery. So both languages now have
the heavy machinery for exceptions, without the language support
for them.  This results in kludges to simulate exceptions.)
 
  mappu - 1 hours ago
  One minor downside to `with` is how it brings normal control flow
  out to the right.
 
    eesmith - 40 minutes ago
    If that's an issue, one option might be
    https://docs.python.org/3/library/contextlib.html?#contextli...
    .
 
  tedunangst - 1 hours ago
  This is still tied to a variable of some type, requiring a dummy
  class for arbitrary functions.
 
    eesmith - 44 minutes ago
    The variable isn't necessary, and contextlib.contextmanager is
    the standard wrapper for arbitrary functions. Here's the
    example code from the documentation:  from contextlib import
    contextmanager    @contextmanager   def tag(name):       print
    "<%s>" % name       yield       print "" % name    >>>
    with tag("h1"):   ...    print("foo")   ...   

   foo
    


 
mappu - 1 hours ago
The ScopeGuard RAII destructor will be called at the end of the
scope, but Go's defer will be called at the end of the
function.e.g. in Go can you can defer inside a for loop.
 
mhh__ - 1 hours ago
I feel that the way this is implemented in the D programming
language is more natural (Subjective, of course):
https://tour.dlang.org/tour/en/gems/scope-guardsAlthough this
approach would probably not work like this in Go given Go's lack of
exceptions.
 
junke - 2 hours ago
I am not sure capturing variables by reference implements the
expected behavior; what would the equivalent C++ code return for
this Go example?    func a() {         i := 0         defer
fmt.Println(i)         i++         return     }
 
  JBReefer - 2 hours ago
  Wouldn't this work by reference? The referenced value would be
  the same in both contexts.Honestly, am I missing something?
 
    Vendan - 1 hours ago
    Go prints 0:https://play.golang.org/p/kdi60DOln-
 
    parenthephobia - 1 hours ago
    The syntax of defer is a function call, but it isn't called
    like a normal function: the arguments are evaluated at the time
    the defer is encountered, and then the function is called
    later. That's one reason why defer is very often called with an
    anonymous function, so that variables can be captured.So, the
    version that prints 1 is    func a() {         i := 0
    defer func(){           fmt.Println(i)         }()         i++
    return     }
 
saurik - 1 hours ago
This is not the same as Go's defer, as this honors scope; this is
similar to D's scope guards (and hence the name of the C++
class...). In Go, when you use defer, the code in question is
deferred until the end of the function, no matter in which scope
you used the feature: this means that if you have a loop in which
you use defer, everything is deferred until well after the entire
loop finishes, not during each execution of the loop. This also
means that Go has extremely... interesting?... behavior with
relation to argument capture for the expressions used in a defer
statement to make this even remotely sane. There are reasons the
people who work on Go think this makes sense, but whether or not
you agree with those semantics, the semantics implemented in this
blog post are very different.