vgvassilev / cling

The interactive C++ interpreter Cling
https://rawgit.com/vgvassilev/cling/master/www/index.html
Other
1.77k stars 102 forks source link

Proper static variable handling in lambdas? #124

Open dabro opened 7 years ago

dabro commented 7 years ago

The following is from Arthur O'Dwyer's 2016 Cppcon "Lambda's from First Principles"

auto make_kitten2(int c) 
{ static int a = 0
; return [c](int d) 
  { static int b = 0
  ; std::cout << "&a = " << &a << ", and &b = " << &b << '\n'
  ; return (++a) + (++b) + c + d
  ;}
;}

(forgive my weird formatting)

auto k3 = make_kitten2(2);
auto k4 = make_kitten2(2);
std::cout << k3(4) << '\n'; // should be 8               &a = 0x11a8b21dc, and &b = 0x11a8b21d8 (int) 8
std::cout << k3(4) << '\n'; // should be 10, ident addr: &a = 0x11a8b2fa4, and &b = 0x11a8b2fa0 (int) 8
std::cout << k4(4) << '\n'; // should be 12,             &a = 0x11a8b2e64, and &b = 0x11a8b2e60 (int) 8

The static int a and b should both be shared between the individual instances k3 and k4 of the same lambda. Meaning that each call, regardless of which lambda is called, should increment a and b.

In the code above, neither the static a from the make_kittens() scope is mutating, nor is b from the lambda scope. O'Dwyer's video said that b should be shared amongst all the lambdas produced by this function; clang shows k3 and k4 are the same (anonymous) type, ie two object instances of the same class, so they share the same operator() and statics of the underlying function object class...

Of note, cling is allowing the lambda to access a and b, despite not capturing, which is proper non-automatic-storage handling. It is also throwing no errors about mutating them despite no mutable keyword, which is also correct.

Cling shows each a and b in each k3 and k4 call as distinct. XCode shows the addresses being identical:

static vars in lambda incremented, k3(d = 4) w c = 2 is (&a = 0x1000052c4, and &b = 0x1000052c8) 8
and again: (&a = 0x1000052c4, and &b = 0x1000052c8) 10
and now k4(d = 4) w c = 2: (&a = 0x1000052c4, and &b = 0x1000052c8) 12
vgvassilev commented 7 years ago

Can you run the same input by prefixing it:

  .rawInput 1
  // code...
  .rawInput 0

Do you still see the same behavior?

dabro commented 7 years ago

Didn't think of that, ok. Unfortunately, still fails proper behavior:

In [5]: auto k3 = make_kitten2(2); Out[5]: ((lambda) &) @0x11aef4000
In [6]: auto k4 = make_kitten2(2); Out[6]: ((lambda) &) @0x11aef4188

In [7]: k3(4) //&a = 0x11aef429c, and &b = 0x11aef4298 (int) 8 Out[7]: (int) 8

In [8]: k4(4); //&a = 0x11aef4f24, and &b = 0x11aef4f20 Out[8]: (int) 8

In [9]: k3(4); //&a = 0x11aefd06c, and &b = 0x11aefd068 Out[9]: (int) 8

In [10]: k3(4); //&a = 0x11aefd1ac, and &b = 0x11aefd1a8 Out[10]: (int) 8