The following code should be almost a copy of what the book Functional Programming in C++ presents at the end of Chapter 6 section 1:
#include <iostream>
#include <utility>
#include <mutex>
template<typename F>
class lazy_val {
private:
F m_computation;
mutable decltype(m_computation()) m_cache;
mutable std::once_flag m_value_flag;
public:
lazy_val(F f)
: m_computation(f)
{}
operator const decltype(m_computation())& () const {
std::call_once(m_value_flag, [this](){
m_cache = m_computation();
});
return m_cache; // the execution never gets to this line
}
};
int expensive() {
std::cout << "expensive call...\n";
return 7;
}
int main() {
std::cout << "non memoized" << '\n';
std::cout << expensive() << '\n';
std::cout << expensive() << '\n';
std::cout << expensive() << '\n';
const auto expensive_memo = lazy_val(expensive);
std::cout << "memoized" << '\n';
std::cout << expensive_memo << '\n'; // crash happens here
std::cout << expensive_memo << '\n';
std::cout << expensive_memo << '\n';
}
However, when I execute it (the compilation is goes fine), I get this error:
non memoized
expensive call...
7
expensive call...
7
expensive call...
7
memoized
terminate called after throwing an instance of 'std::system_error'
what(): Unknown error -1
Aborted (core dumped)
If ignore concurrency issues and only rely on a mutable bool m_cache_initialized;
initialized to false
, and a if (!m_cache_initialized) { m_cache = m_computation(); m_cache_initialized = true; }
, then everything works.
This makes me think that the problem is with how I use std::call_once
/std::once_flag
in my code. However I don't see how it is different from what is shown in the book (constructor in Listing 6.2, but without the line initializing the m_cache_initialized
to false
, and the rest of the class is at the bottom of page 125 and top of 126).