Program Listing for File swap.hpp

Return to documentation for file (SeQuant/core/utility/swap.hpp)

//
// Created by Eduard Valeyev on 2/14/25.
//

#ifndef SEQUANT_CORE_UTILITY_SWAPPABLE_HPP
#define SEQUANT_CORE_UTILITY_SWAPPABLE_HPP

#include <atomic>

namespace sequant {

template <typename T>
class SwapCountable;

template <typename T>
class SwapCountableRef;

template <typename T>
void swap(SwapCountable<T>& a, SwapCountable<T>& b);
template <typename T>
bool operator<(const SwapCountable<T>& a, const SwapCountable<T>& b);
template <typename T>
void swap(const SwapCountableRef<T>& a, const SwapCountableRef<T>& b);
template <typename T>
bool operator<(const SwapCountableRef<T>& a, const SwapCountableRef<T>& b);

template <typename T>
void counted_swap(T& a, T& b);

namespace detail {

template <typename T>
void count_swap();

template <typename T>
struct SwapCounter {
  SwapCounter() : even_num_of_swaps_(true) {}
  static SwapCounter& thread_instance() {
    static thread_local SwapCounter instance_{};
    return instance_;
  }

  bool even_num_of_swaps() const { return even_num_of_swaps_; }
  void reset() { even_num_of_swaps_ = true; }

 private:
  std::atomic<bool> even_num_of_swaps_;
  void toggle() { even_num_of_swaps_ = !even_num_of_swaps_; }

  friend void swap<T>(SwapCountable<T>&, SwapCountable<T>&);
  friend void swap<T>(const SwapCountableRef<T>&, const SwapCountableRef<T>&);
  friend void counted_swap<T>(T& a, T& b);
  friend void count_swap<T>();
};

template <typename T>
void count_swap() {
  detail::SwapCounter<T>::thread_instance().toggle();
}

}  // namespace detail

template <typename T>
class SwapCountable {
 public:
  template <typename U>
  explicit SwapCountable(U&& v) : value_(std::forward<U>(v)) {}

 private:
  T value_;

  friend void swap<T>(SwapCountable& a, SwapCountable& b);
  friend bool operator< <T>(const SwapCountable& a, const SwapCountable& b);
};

template <typename T>
void swap(SwapCountable<T>& a, SwapCountable<T>& b) {
  using std::swap;
  swap(a.value_, b.value_);
  detail::SwapCounter<T>::thread_instance().toggle();
}

template <typename T>
bool operator<(const SwapCountable<T>& a, const SwapCountable<T>& b) {
  return a.value_ < b.value_;
}

template <typename T>
class SwapCountableRef {
 public:
  explicit SwapCountableRef(T& ref) : ref_(ref) {}

 private:
  T& ref_;

  friend void swap<T>(const SwapCountableRef& a, const SwapCountableRef& b);
  friend bool operator< <T>(const SwapCountableRef& a,
                            const SwapCountableRef& b);
};

// NB swapping const wrappers swaps the payload
template <typename T>
void swap(const SwapCountableRef<T>& a, const SwapCountableRef<T>& b) {
  using std::swap;
  swap(a.ref_, b.ref_);
  detail::SwapCounter<T>::thread_instance().toggle();
}

template <typename T>
bool operator<(const SwapCountableRef<T>& a, const SwapCountableRef<T>& b) {
  return a.ref_ < b.ref_;
}
template <typename T>
void reset_ts_swap_counter() {
  detail::SwapCounter<T>::thread_instance().reset();
}

template <typename T>
bool ts_swap_counter_is_even() {
  return detail::SwapCounter<T>::thread_instance().even_num_of_swaps();
}

template <typename T>
void counted_swap(T& a, T& b) {
  using std::swap;
  swap(a, b);
  detail::SwapCounter<T>::thread_instance().toggle();
}

}  // namespace sequant

#endif  // SEQUANT_CORE_UTILITY_SWAPPABLE_HPP