Clean way to lazy initialize and cache internal value in lambda

  • A+
Category:Languages

Let the code speak for itself first with naive approach:

int heavy_calc() // needed to be called once {     // sleep(7500000 years)     return 42; }  int main() {     auto foo = [] {         // And cached for lambda return value         static int cache = heavy_calc();         return cache;     };     return foo() + foo(); } 

I want to have lambda internal cached value calculated on the first call. An naive approach is to use static cache, but it increases binary size and refuses to be be inlined.

I came up with creating cache in capture list and marking lambda as mutable, what inlines without problems, but requires cache to start with default value, which may break class invariant.

 auto foo = [cache=0] () mutable {     // And cached for lambda return value     if(!cache)         cache = heavy_calc();     return cache; }; 

My third approach uses boost::optional in mutable lambda

auto foo = [cache=std::optional<int>{}] () mutable {     // And cached for lambda return value     if(!cache)         cache = heavy_calc();     return *cache; }; 

It works properly, but looks for me as kind of capture list + mutable keyword hack. Also mutable affects all captured parameters, so makes lambda less safe in real use.

Maybe there is an better/more clean solution for this? Or just different approach which ends up with the very same effect.

EDIT, some background: Lambda approach is chosen as I am modifying some callback lambda, which currently is used as: [this, param]{this->onEvent(heavy_calc(param));} I want to reduce heavy_calc calls without evaluating it in advance (only on first call)

 


To be honest, I don't see any reason to use lambda here. You can write a regular reusable class to cache calculation value. If you insist on using lambda then you can move value calculation to parameters so there will be no need to make anything mutable:

int heavy_calc() // needed to be called once {     // sleep(7500000 years)     return 42; }  int main() {     auto foo     {         [cache = heavy_calc()](void)         {             return cache;         }     };     return foo() + foo(); } 

online compiler

With a bit of template it is possible to write a class that will lazy evaluate and cache result of arbitrary calculation:

#include <boost/optional.hpp> #include <utility>  template<typename x_Action> class t_LazyCached final {     private: x_Action m_action;     private: ::boost::optional<decltype(::std::declval<x_Action>()())> m_cache;      public: template<typename xx_Action> explicit     t_LazyCached(xx_Action && action): m_action{::std::forward<xx_Action>(action)}, m_cache{} {}      public: auto const &     operator ()(void)     {         if(not m_cache)         {             m_cache = m_action();         }         return m_cache.value();     } };  template<typename x_Action> auto Make_LazyCached(x_Action && action) {     return t_LazyCached<x_Action>{::std::forward<x_Action>(action)}; }  class t_Obj {     public: int heavy_calc(int param) // needed to be called once     {         // sleep(7500000 years)         return 42 + param;     } };  int main() {     t_Obj obj{};     int param{3};     auto foo{Make_LazyCached([&](void){ return obj.heavy_calc(param); })};     return foo() + foo(); } 

online compiler

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: