4

I would like to know if the following leaks memory or not (specified by the standard)

...
jmp_buf env;
if(setjmp(env) == 0) {
    auto lambda = [&] () {
        ... 
        longjmp(env, 1);
    };
    lambda();
}

which boils down to whether lambdas capturing by reference have a trivial destructor (I guess)?

I know that this is probably wicked, but has to be done nevertheless.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
iggy
  • 1,613
  • 1
  • 18
  • 35
  • 4
    Why in the world would you *ever* call `setjmp` and `longjmp`, in 2015, from C++? – Griwes Apr 28 '15 at 09:39
  • 1
    @Griwes This is the way postgres handles exceptions and I am writing one in C++, as I am using a c++ library – iggy Apr 28 '15 at 09:47
  • 1
    It's extremely unlikely that PostgresSQL expects you to call setjmp(), and then its library invokes longjmp(). Any usage of setjmp/longjmp is likely internal to PostgresSQL's library, and shouldn't be of concern to anyone. – Sam Varshavchik Apr 28 '15 at 11:01
  • 1
    @SamVarshavchik when one calls ereport(ERROR, ..) a longjump will occur and all c++ objects that are on the stack and not POD will leak. http://www.postgresql.org/docs/9.4/static/xfunc-c.html quote `If calling backend functions from C++ code, be sure that the C++ call stack contains only plain old data structures (POD). This is necessary because backend errors generate a distant longjmp() that does not properly unroll a C++ call stack with non-POD objects.` – iggy Apr 28 '15 at 12:33
  • That text is referencing postgresql extension code, not ordinary C++ code making regular postgresql calls. Postgresql's high level functions invoke setjmp(), and invoke various low-level functions. If you have an extension function compiled in, and it does whatever it does, and calls ereport(), ereport() executes a longjmp. Which, of course, you have to keep in mind if your extension code is written in C++. Are you implementing a PostgreSQL extension? – Sam Varshavchik Apr 29 '15 at 00:09

1 Answers1

4

It's implementation-specific. You may reasonably expect it to be true, but here's what the standard says (N4140, [expr.prim.lambda]/3, emphasis mine):

An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
— the size and/or alignment of the closure type,
whether the closure type is trivially copyable (Clause 9),
— whether the closure type is a standard-layout class (Clause 9), or
— whether the closure type is a POD class (Clause 9).

And by definition in [class]/3

A trivially copyable class is a class that:
— has no non-trivial copy constructors (12.8),
— has no non-trivial move constructors (12.8),
— has no non-trivial copy assignment operators (13.5.3, 12.8),
— has no non-trivial move assignment operators (13.5.3, 12.8), and
has a trivial destructor (12.4).

So, an implementation is allowed to create a non-trivial destructor for the lambda.

However, you can check if your particluar implementation made your particular lambda trivially destructible by the following:

auto lambda = [&]{ /*...*/ };
static_assert(std::is_trivially_destructible<decltype(lambda)>::value, "Lambda isn't trivially destructible");
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • Why is the destructor the only one described as trivial while everything else is "not non-trivial"? – David G Apr 28 '15 at 10:26
  • 1
    "Has a trivial copy constructor" is not the same as "Has no non-trivial copy constructor", because it may have several. Whereas "Has a trivial destructor" is the same as "Has no non-trivial destructor", because it can only have one destructor, but the latter description is therefore unnecessarily wordy. – Daniel Earwicker Apr 28 '15 at 11:26