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).
You must be using Linux and gcc. An "undocumented" feature of this implementation is that any C++ code that uses any thread-related stuff must be explicitly linked with -lpthread
.
I reproduced your exact crash on Linux, after compiling (with -std=c++17
) and linking without -lpthread
. The shown code runs fine, if explicitly linked with -lpthread
.