<commit_before>// @COPYRIGHT@
// Licensed under MIT license (LICENSE.txt)

// clxx/common/memoized_function.hpp

/** // doc: clxx/common/memoized_function.hpp {{{
 * \file clxx/common/memoized_function.hpp
 * \brief Provides the \ref clxx::memoized_function "memoized_function"
 */ // }}}
#ifndef CLXX_COMMON_MEMOIZED_FUNCTION_HPP_INCLUDED
#define CLXX_COMMON_MEMOIZED_FUNCTION_HPP_INCLUDED

#include <clxx/common/exceptions.hpp>
#include <functional>
#include <type_traits>
#include <map>
#include <mutex>

namespace clxx {
/** \cond DOXYGEN_SHOW_IGNORED_COMPOUNDS */
template<typename F>
struct memoized_function;
/** \endcond */
/** // doc: memoized_function<R(Args...)> {{{
 * \brief Memoized function
 *
 * The \ref clxx::memoized_function<R(Args...)> adds result caching feature to
 * a callable object (function, method and such).
 *
 * The internal map used to cache the results is maintained in a thread-safe
 * way. The memoized function object is copy-constructible and copyable.
 *
 * \tparam R
 *    Type of the result of the wrapped calleble object
 * \tparam Args
 *    Types of arguments accepted by the wrapped callable object
 *
 * \par Example
 * \snippet clxx/common/memoized_function1.cpp Program
 *
 * \par Output
 * \snippet clxx/common/memoized_function1.cpp Output
 */ // }}}
template<typename R, typename... Args>
struct memoized_function<R(Args...)>
  : std::function<R(Args...)>
{
private:
  typedef std::function<R(Args...)> Base;
  typedef std::tuple<typename std::decay<Args>::type...> args_tuple_t;
  typedef std::map<args_tuple_t, R> cache_t;
  mutable cache_t _cache;
  mutable std::mutex _mutex;
public:
  using Base::Base;
  /** // doc: memoized_function(memoized_function const&) {{{
   * \brief Copy constructor
   *
   * The copy constructor copies the callable object wrapped by \p other and
   * and copy of the internally maintained result cache.
   *
   * \param other
   *    The other object which initialized this one
   */ // }}}
  memoized_function(memoized_function const& other)
  {
    if(this == &other) return; // avoid self-deadlock
    // To be thread-safe we have to protect mutable member(s)
    std::lock(_mutex, other._mutex); // acquire both mutexes or wait
    std::lock_guard<std::mutex> lock1(_mutex, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(other._mutex, std::adopt_lock);
    _cache = other._cache;
  }
  /** // doc: memoized_function(memoized_function const&) {{{
   * \brief Assignment operator
   *
   * The assignment operator assigns the copy of the callable object and 
   * and copy of the internally maintained result cache.
   *
   * \param other
   *    The other object to be assigned to this one.
   * \returns
   *    A mutable reference to \c this object.
   */ // }}}
  memoized_function& operator=(memoized_function const& other)
  {
    if(this == &other) return *this; // avoid self-deadlock
    // To be thread-safe we have to protect mutable member(s)
    std::lock(_mutex, other._mutex); // acquire both mutexes or wait
    std::lock_guard<std::mutex> lock1(_mutex, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(other._mutex, std::adopt_lock);
    _cache = other._cache;<commit_msg>fix copy c-tor in memoized_function<commit_after>// @COPYRIGHT@
// Licensed under MIT license (LICENSE.txt)

// clxx/common/memoized_function.hpp

/** // doc: clxx/common/memoized_function.hpp {{{
 * \file clxx/common/memoized_function.hpp
 * \brief Provides the \ref clxx::memoized_function "memoized_function"
 */ // }}}
#ifndef CLXX_COMMON_MEMOIZED_FUNCTION_HPP_INCLUDED
#define CLXX_COMMON_MEMOIZED_FUNCTION_HPP_INCLUDED

#include <clxx/common/exceptions.hpp>
#include <functional>
#include <type_traits>
#include <map>
#include <mutex>

namespace clxx {
/** \cond DOXYGEN_SHOW_IGNORED_COMPOUNDS */
template<typename F>
struct memoized_function;
/** \endcond */
/** // doc: memoized_function<R(Args...)> {{{
 * \brief Memoized function
 *
 * The \ref clxx::memoized_function<R(Args...)> adds result caching feature to
 * a callable object (function, method and such).
 *
 * The internal map used to cache the results is maintained in a thread-safe
 * way. The memoized function object is copy-constructible and copyable.
 *
 * \tparam R
 *    Type of the result of the wrapped calleble object
 * \tparam Args
 *    Types of arguments accepted by the wrapped callable object
 *
 * \par Example
 * \snippet clxx/common/memoized_function1.cpp Program
 *
 * \par Output
 * \snippet clxx/common/memoized_function1.cpp Output
 */ // }}}
template<typename R, typename... Args>
struct memoized_function<R(Args...)>
  : std::function<R(Args...)>
{
private:
  typedef std::function<R(Args...)> Base;
  typedef std::tuple<typename std::decay<Args>::type...> args_tuple_t;
  typedef std::map<args_tuple_t, R> cache_t;
  mutable cache_t _cache;
  mutable std::mutex _mutex;
public:
  using Base::Base;
  /** // doc: memoized_function(memoized_function const&) {{{
   * \brief Copy constructor
   *
   * The copy constructor copies the callable object wrapped by \p other and
   * and copy of the internally maintained result cache.
   *
   * \param other
   *    The other object which initialized this one
   */ // }}}
  memoized_function(memoized_function const& other)
    : Base(other)
  {
    if(this == &other) return; // avoid self-deadlock
    // To be thread-safe we have to protect mutable member(s)
    std::lock(_mutex, other._mutex); // acquire both mutexes or wait
    std::lock_guard<std::mutex> lock1(_mutex, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(other._mutex, std::adopt_lock);
    _cache = other._cache;
  }
  /** // doc: memoized_function(memoized_function const&) {{{
   * \brief Assignment operator
   *
   * The assignment operator assigns the copy of the callable object and 
   * and copy of the internally maintained result cache.
   *
   * \param other
   *    The other object to be assigned to this one.
   * \returns
   *    A mutable reference to \c this object.
   */ // }}}
  memoized_function& operator=(memoized_function const& other)
  {
    if(this == &other) return *this; // avoid self-deadlock
    // To be thread-safe we have to protect mutable member(s)
    std::lock(_mutex, other._mutex); // acquire both mutexes or wait
    std::lock_guard<std::mutex> lock1(_mutex, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(other._mutex, std::adopt_lock);
    _cache = other._cache;
{'fraction_non_alphanumeric': 0.12449424214130096, 'fraction_numerical': 0.0018674136321195146, 'mean_word_length': 3.7819940476190474, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3508122', 'n_tokens_mistral': 2085, 'n_tokens_neox': 2067, 'n_words': 580}