/** * @file kcpp_memory.hpp * @brief KC++ メモリ管理モジュール * @copyright 2003 - 2023 Nomura Kei */ #ifndef KCPP_MEMORY_HPP #define KCPP_MEMORY_HPP #include <iostream> #include <string> #include <thread> #include <mutex> #include <cstring> #include <kcpp.hpp> namespace kcpp { /** * メモリ状態 */ enum MemoryMark { MEMORY_MARK_DELETED = 0x55AA0000, MEMORY_MARK_ALLOCATED = 0x55AA1111, MEMORY_MARK_ALLOCATED_NEW = 0x55AA2222, MEMORY_MARK_ALLOCATED_NEW_ARRAY = 0x55AA4444 }; /** * メモリエントリ。 */ struct MemoryEntry { const char* file; //!< メモリ確保ファイル名 const char* func; //!< メモリ確保関数名 int line; //!< メモリ確保行番号 int size; //!< 確保サイズ MemoryMark _mark; //!< 確保メモリ状態 MemoryEntry* _prev; //!< 前の管理メモリポインタ MemoryEntry* _next; //!< 次の管理メモリポインタ void* data; //!< データ }; /** * メモリが確保, 解放, あるいはメモリ確保/解放時にエラーが発生した際のリスナインタフェース。 * 本リスナを継承したクラスを MemoryManager::setListener にて登録することにより、 * メモリ確保, 解放, エラー発生時の通知を受信できます。 * * リスナの登録は、プログラム開始時に実施してください。 */ class MemoryListener { public: MemoryListener(); virtual ~MemoryListener(); virtual void notifyAllocate(const MemoryEntry& entry); virtual void notifyFree(const MemoryEntry& entry); virtual void notifyError(const MemoryEntry& entry, const char* msg); }; /** * メモリエントリに指定されたパラメータを設定、初期化します。 * * @param entry 初期化設定するメモリエントリ * @param size 確保サイズ * @param mark 確保メモリ状態 * @param file メモリ確保ファイル名 * @param func メモリ確保関数名 * @param line メモリ確保行番号 */ void initMemoryEntry(MemoryEntry* entry, std::size_t size, int mark, const char* file, const char* func, int line); /** * メモリの確保、解放を管理します。 */ namespace MemoryManager { extern thread_local const char* file; extern thread_local const char* func; extern thread_local int line; void setListener(MemoryListener& listener); void entries(bool (*handler)(const MemoryEntry& entry)); void freeif(bool (*handler)(const MemoryEntry& entry)); void dump(std::ostream& stream, int dumpByte = 16, bool isDumpBinary = true, bool isDumpAscii = true, int dumpColumn = 120); void* malloc ( std::size_t size, const char* file, const char* func, int line); void* calloc (std::size_t nmemb, std::size_t size, const char* file, const char* func, int line); void* realloc(void* ptr , std::size_t size, const char* file, const char* func, int line); void free (void* ptr); } } #if (__cplusplus >= 202002L) // C++20 (C++2a) #define NODISCARD [[nodiscard]] #else #define NODISCARD #endif //////////////////////////////////////////////////////////////////////////////// // // KCPP_MEMORY_ENABLED が定義されている場合、メモリ管理が有効となります。 // // #ifdef KCPP_MEMORY_ENABLED // C++17 (C++1z) 以降の new/delete 演算子 // 下記、順に // (1) 記憶域確保 // (2) 例外送出なしで記憶域確保 // (3) デフォルトより大きいアライメント要求の記憶域確保 // (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保 // // // 以下については、メモリ管理外としてオーバーライドしない。 // // (A) 配置 new による記憶域確保 (あらかじめ用意したメモリに対してインスタンスを割り当てる) // NODISCARD void* operator new(std::size_t size, void* ptr) noexcept; // NODISCARD void* operator new[](std::size_t size, void* ptr) noexcept; // NODISCARD void* operator new(std::size_t size); NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept; NODISCARD void* operator new(std::size_t size, std::align_val_t alignment); NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; NODISCARD void* operator new[](std::size_t size); NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment); NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept; // 下記、順に // (1) 記憶域解放 // (2) 例外送出なしで確保された記憶域解放 // (3) デフォルトより大きいアライメント要求の記憶域解放 // (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放 // (5) オブジェクトサイズが判明している記憶域解放 // (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放 // // 以下については、メモリ管理外としてオーバーライドしない。 // (A) 配置 new で確保された記憶域の開放 // void operator delete(void* ptr, void*) noexcept; // void operator delete[](void* ptr, void*) noexcept; // void operator delete(void* ptr) noexcept; void operator delete(void* ptr, const std::nothrow_t&) noexcept; void operator delete(void* ptr, std::align_val_t alignment) noexcept; void operator delete(void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; void operator delete(void* ptr, std::size_t size) noexcept; void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept; void operator delete[](void* ptr) noexcept; void operator delete[](void* ptr, const std::nothrow_t&) noexcept; void operator delete[](void* ptr, std::align_val_t alignment) noexcept; void operator delete[](void* ptr, std::align_val_t alignement, const std::nothrow_t&) noexcept; void operator delete[](void* ptr, std::size_t size) noexcept; void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept; #define new \ ((kcpp::MemoryManager::file = __FILE__, \ kcpp::MemoryManager::func = __func__, \ kcpp::MemoryManager::line = __LINE__, \ 0) && 0) ? 0 : new #define malloc(size) kcpp::MemoryManager::malloc ( size, __FILE__, __func__, __LINE__) #define calloc(nmemb, size) kcpp::MemoryManager::calloc (nmemb, size, __FILE__, __func__, __LINE__) #define realloc(ptr, size) kcpp::MemoryManager::realloc(ptr , size, __FILE__, __func__, __LINE__) #define free(ptr) kcpp::MemoryManager::free (ptr) #else #include <new> #include <cstdlib> #endif // KCPP_MEMORY_ENABLED #endif // KC_MEMORY_HPP