Is it possible to use std::call_once in a scenario where the number of expensive functions varies?
Imagine a factory computing expensive things according to a template parameter ResultType, e.g. edge adjacency or averaged vertex normals for a triangle mesh:
std::unordered_map<std::string, std::unique_ptr<AbstractResult>> mResults;
std::mutex mMutex;
template<typename ResultType, typename ...Params>
const ResultType& getResult(Params&&... params)
{
std::lock_guard<std::mutex> lock(mMutex);
const std::string& hash = ResultType::computeHash(std::forward<Params>(params)...);
auto it = mResults.find(hash);
if (it == mResults.end())
{
// Expensive call
mResults[hash] = std::make_unique<ResultType>(std::forward<Params>(params)...);
it = mResults.find(hash);
}
return *static_cast<ResultType*>(it->second.get());
}
The lock is only necessary as long as the result has not been created. To avoid unnecessary locking for reading only, double-checked locking can be used. There are various examples how to do this correcly in C++11, that's not a problem.
Yet, my understanding is that std::call_once offers an elegant solution for this and should be used to avoid manual double-checked locking. I am aware how to use std::call_once with a single std::once_flag variable. However, how would I use std::call_once in my example? For each different ResultType, I need an std::once_flag. How do I store the arbitrary number of once_flags? I cannot store them in a second unordered_map addressed with the results' hashes, as once_flags are neither copyable nor movable. Am I missing something obvious here?
All possible ResultTypes (and therefore the number of std::once_flags), which are in the order of 30, are known at compile time. However, there is no list of all possible ResultTypes, this is only known from all calls to getResult<ResultType>
. If I can solve this with template magic, I'm thankful for any input, because I don't know how. If there is any MSVC-specific solution, I'm happy to take that as well.
Aucun commentaire:
Enregistrer un commentaire