#include #include // for print #include // for abort #include // for ostream #include // for runtime_error #include // for unordered_set #include // for swap, pair static inline constexpr bool counter_enable_unordered_set = true; auto singleton_constructed_objects() -> std::unordered_set& { static std::unordered_set static_data{}; return static_data; } counter::obj::obj() : m_data(0) , m_counts(nullptr) { if constexpr (counter_enable_unordered_set) { if (!singleton_constructed_objects().emplace(this).second) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } ++static_default_ctor; } counter::obj::obj(const size_t& data, counter& counts) : m_data(data) , m_counts(&counts) { if constexpr (counter_enable_unordered_set) { if (!singleton_constructed_objects().emplace(this).second) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } ++m_counts->m_data.m_ctor; } counter::obj::obj(const counter::obj& o) : m_data(o.m_data) , m_counts(o.m_counts) { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(&o)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } if (!singleton_constructed_objects().emplace(this).second) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } if (nullptr != m_counts) { ++m_counts->m_data.m_copy_ctor; } } counter::obj::obj(counter::obj&& o) noexcept : m_data(o.m_data) , m_counts(o.m_counts) { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(&o)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } if (!singleton_constructed_objects().emplace(this).second) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } if (nullptr != m_counts) { ++m_counts->m_data.m_move_ctor; } } counter::obj::~obj() { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().erase(this)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } if (nullptr != m_counts) { ++m_counts->m_data.m_dtor; } else { ++static_dtor; } } auto counter::obj::operator==(obj const& o) const -> bool { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } if (nullptr != m_counts) { ++m_counts->m_data.m_equals; } return m_data == o.m_data; } auto counter::obj::operator<(obj const& o) const -> bool { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } if (nullptr != m_counts) { ++m_counts->m_data.m_less; } return m_data < o.m_data; } // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp) auto counter::obj::operator=(obj const& o) -> counter::obj& { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } m_counts = o.m_counts; if (nullptr != m_counts) { ++m_counts->m_data.m_assign; } m_data = o.m_data; return *this; } auto counter::obj::operator=(obj&& o) noexcept -> counter::obj& { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&o)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } if (nullptr != o.m_counts) { m_counts = o.m_counts; } m_data = o.m_data; if (nullptr != m_counts) { ++m_counts->m_data.m_move_assign; } return *this; } auto counter::obj::get() const -> size_t const& { if (nullptr != m_counts) { ++m_counts->m_data.m_const_get; } return m_data; } auto counter::obj::get() -> size_t& { if (nullptr != m_counts) { ++m_counts->m_data.m_get; } return m_data; } void counter::obj::swap(obj& other) { if constexpr (counter_enable_unordered_set) { if (1 != singleton_constructed_objects().count(this) || 1 != singleton_constructed_objects().count(&other)) { test::print("ERROR at {}({}): {}\n", __FILE__, __LINE__, __func__); std::abort(); } } using std::swap; swap(m_data, other.m_data); swap(m_counts, other.m_counts); if (nullptr != m_counts) { ++m_counts->m_data.m_swaps; } } auto counter::obj::get_for_hash() const -> size_t { if (nullptr != m_counts) { ++m_counts->m_data.m_hash; } return m_data; } counter::counter() { counter::static_default_ctor = 0; counter::static_dtor = 0; } void counter::check_all_done() const { if constexpr (counter_enable_unordered_set) { // check that all are destructed if (!singleton_constructed_objects().empty()) { test::print("ERROR at ~counter(): got {} objects still alive!", singleton_constructed_objects().size()); std::abort(); } if (m_data.m_dtor + static_dtor != m_data.m_ctor + static_default_ctor + m_data.m_copy_ctor + m_data.m_default_ctor + m_data.m_move_ctor) { test::print("ERROR at ~counter(): number of counts does not match!\n"); test::print( "{} dtor + {} staticDtor != {} ctor + {} staticDefaultCtor + {} copyCtor + {} defaultCtor + {} moveCtor\n", m_data.m_dtor, static_dtor, m_data.m_ctor, static_default_ctor, m_data.m_copy_ctor, m_data.m_default_ctor, m_data.m_move_ctor); std::abort(); } } } counter::~counter() { check_all_done(); } auto counter::total() const -> size_t { return m_data.m_ctor + static_default_ctor + m_data.m_copy_ctor + (m_data.m_dtor + static_dtor) + m_data.m_equals + m_data.m_less + m_data.m_assign + m_data.m_swaps + m_data.m_get + m_data.m_const_get + m_data.m_hash + m_data.m_move_ctor + m_data.m_move_assign; } void counter::operator()(std::string_view title) { m_records += fmt::format("{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}{:9}|{:9}| {}\n", m_data.m_ctor, static_default_ctor, m_data.m_copy_ctor, m_data.m_dtor + static_dtor, m_data.m_assign, m_data.m_swaps, m_data.m_get, m_data.m_const_get, m_data.m_hash, m_data.m_equals, m_data.m_less, m_data.m_move_ctor, m_data.m_move_assign, total(), title); } auto operator<<(std::ostream& os, counter const& c) -> std::ostream& { return os << c.m_records; } auto operator new(size_t /*unused*/, counter::obj* /*unused*/) -> void* { throw std::runtime_error("operator new overload is taken! Cast to void* to ensure the void pointer overload is taken."); } size_t counter::static_default_ctor = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) size_t counter::static_dtor = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)