diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/modules/j/base/test/test-lib/src/calc.c b/modules/j/base/test/test-lib/src/calc.c new file mode 100644 index 0000000..ed54a95 --- /dev/null +++ b/modules/j/base/test/test-lib/src/calc.c @@ -0,0 +1,16 @@ +int calc_add(int v1, int v2) +{ + return (v1 + v2); +} +int calc_sub(int v1, int v2) +{ + return (v1 - v2); +} +int calc_mul(int v1, int v2) +{ + return (v1 * v2); +} +int calc_div(int v1, int v2) +{ + return (v1 / v2); +} diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/modules/j/base/test/test-lib/src/calc.c b/modules/j/base/test/test-lib/src/calc.c new file mode 100644 index 0000000..ed54a95 --- /dev/null +++ b/modules/j/base/test/test-lib/src/calc.c @@ -0,0 +1,16 @@ +int calc_add(int v1, int v2) +{ + return (v1 + v2); +} +int calc_sub(int v1, int v2) +{ + return (v1 - v2); +} +int calc_mul(int v1, int v2) +{ + return (v1 * v2); +} +int calc_div(int v1, int v2) +{ + return (v1 / v2); +} diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile new file mode 100644 index 0000000..1c6c98f --- /dev/null +++ b/modules/j/cppunit/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libj-cppunit +TARGET = $(NAME).so +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -ljbase + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/modules/j/base/test/test-lib/src/calc.c b/modules/j/base/test/test-lib/src/calc.c new file mode 100644 index 0000000..ed54a95 --- /dev/null +++ b/modules/j/base/test/test-lib/src/calc.c @@ -0,0 +1,16 @@ +int calc_add(int v1, int v2) +{ + return (v1 + v2); +} +int calc_sub(int v1, int v2) +{ + return (v1 - v2); +} +int calc_mul(int v1, int v2) +{ + return (v1 * v2); +} +int calc_div(int v1, int v2) +{ + return (v1 / v2); +} diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile new file mode 100644 index 0000000..1c6c98f --- /dev/null +++ b/modules/j/cppunit/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libj-cppunit +TARGET = $(NAME).so +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -ljbase + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md new file mode 100644 index 0000000..dd82708 --- /dev/null +++ b/modules/j/cppunit/README.md @@ -0,0 +1,8 @@ +* Model : j.cppunit + +** includes +- j/cppunit + +** depends +- j.base + diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/modules/j/base/test/test-lib/src/calc.c b/modules/j/base/test/test-lib/src/calc.c new file mode 100644 index 0000000..ed54a95 --- /dev/null +++ b/modules/j/base/test/test-lib/src/calc.c @@ -0,0 +1,16 @@ +int calc_add(int v1, int v2) +{ + return (v1 + v2); +} +int calc_sub(int v1, int v2) +{ + return (v1 - v2); +} +int calc_mul(int v1, int v2) +{ + return (v1 * v2); +} +int calc_div(int v1, int v2) +{ + return (v1 / v2); +} diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile new file mode 100644 index 0000000..1c6c98f --- /dev/null +++ b/modules/j/cppunit/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libj-cppunit +TARGET = $(NAME).so +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -ljbase + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md new file mode 100644 index 0000000..dd82708 --- /dev/null +++ b/modules/j/cppunit/README.md @@ -0,0 +1,8 @@ +* Model : j.cppunit + +** includes +- j/cppunit + +** depends +- j.base + diff --git a/modules/j/cppunit/src/assert.cpp b/modules/j/cppunit/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/cppunit/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/modules/j/base/test/test-lib/src/calc.c b/modules/j/base/test/test-lib/src/calc.c new file mode 100644 index 0000000..ed54a95 --- /dev/null +++ b/modules/j/base/test/test-lib/src/calc.c @@ -0,0 +1,16 @@ +int calc_add(int v1, int v2) +{ + return (v1 + v2); +} +int calc_sub(int v1, int v2) +{ + return (v1 - v2); +} +int calc_mul(int v1, int v2) +{ + return (v1 * v2); +} +int calc_div(int v1, int v2) +{ + return (v1 / v2); +} diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile new file mode 100644 index 0000000..1c6c98f --- /dev/null +++ b/modules/j/cppunit/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libj-cppunit +TARGET = $(NAME).so +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -ljbase + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md new file mode 100644 index 0000000..dd82708 --- /dev/null +++ b/modules/j/cppunit/README.md @@ -0,0 +1,8 @@ +* Model : j.cppunit + +** includes +- j/cppunit + +** depends +- j.base + diff --git a/modules/j/cppunit/src/assert.cpp b/modules/j/cppunit/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/cppunit/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/cppunit/src/cppunit.cpp b/modules/j/cppunit/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/cppunit/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/Makefile b/Makefile index aae770b..63b170d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ RULEDIR ?= $(TOPDIR)/mk NAME = TARGET = $(NAME) -SUBDIRS = j +SUBDIRS = modules USE_SO_VERSION = # ------------------------------------------------------------------------------ diff --git a/config.mk b/config.mk index 9410b18..b2830d7 100644 --- a/config.mk +++ b/config.mk @@ -79,8 +79,8 @@ LIBS += -CFLAGS += -DKC_MEMORY_ENABLED=1 -CXXFLAGS += -DKCPP_MEMORY_ENABLED=1 +CFLAGS += -DJ_MEMORY_ENABLED=1 +CXXFLAGS += -DJ_MEMORY_ENABLED=1 # ------------------------------------------------------------------------------ diff --git a/include/j.hpp b/include/j.hpp index 65c889f..f9ff7cc 100644 --- a/include/j.hpp +++ b/include/j.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #else // ============================================================================= diff --git a/include/j/cppunit/assert.hpp b/include/j/cppunit/assert.hpp new file mode 100644 index 0000000..b3b58e9 --- /dev/null +++ b/include/j/cppunit/assert.hpp @@ -0,0 +1,67 @@ +/** + * @file assert.hpp + * @brief J Library Assert ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/overload.hpp + * j/lang/object.hpp + * (j/lang/assertion_error.hpp) + */ +#ifndef J_CPPUNIT_ASSERT_HPP +#define J_CPPUNIT_ASSERT_HPP + +#include +#include + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + // 汎用, Object を継承したクラスも operator!= により同じ。 + template + void _assertEqualsImpl(const T &expected, const T &actual, + const char *file, const char *func, int line) + { + if (expected != actual) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line); + + // float, double の場合 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line); + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line); + void _assertFalseImpl(bool condition, const char *file, const char *func, int line); + void _assertNullImpl(void *obj, const char *file, const char *func, int line); + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line); + void _failImpl(const char *file, const char *func, int line); + } + } // cppunit +} // namespace j + +// __FILE__, __func__, __LINE__ 情報埋め込みマクロ +#define assertEquals(...) J_OVERLOAD(assertEqualsArgs, __VA_ARGS__) +#define assertEqualsArgs2(expected, actual) j::cppunit::Assert::_assertEqualsImpl(expected, actual, __FILE__, __func__, __LINE__) +#define assertEqualsArgs3(expected, actual, delta) j::cppunit::Assert::_assertEqualsFloatImpl(expected, actual, delta, __FILE__, __func__, __LINE__) +#define assertTrue(condition) j::cppunit::Assert::_assertTrueImpl(condition, __FILE__, __func__, __LINE__) +#define assertFalse(condition) j::cppunit::Assert::_assertFalseImpl(condition, __FILE__, __func__, __LINE__) +#define assertNull(obj) j::cppunit::Assert::_assertNullImpl(obj, __FILE__, __func__, __LINE__) +#define assertNotNull(obj) j::cppunit::Assert::_assertNotNullImpl(obj, __FILE__, __func__, __LINE__) +#define fail() j::cppunit::Assert::_failImpl(__FILE__, __func__, __LINE__) + +#endif // J_CPPUNIT_ASSERT_HPP diff --git a/include/j/cppunit/cppunit.hpp b/include/j/cppunit/cppunit.hpp new file mode 100644 index 0000000..c642b5b --- /dev/null +++ b/include/j/cppunit/cppunit.hpp @@ -0,0 +1,120 @@ +/** + * @file cppunit.hpp + * @brief J Library Cppunit ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_CPPUNIT_CPPUNIT_HPP +#define J_CPPUNIT_CPPUNIT_HPP + +#include +#include +#include + +namespace j +{ + namespace cppunit + { + /** + * テストケース。 + * 各テストクラスは、本クラスを継承して作成します。 + * + * 以下に実装例を示します。 + * + * @code + * #include + * #include + * + * using namespace j; + * using namespace j::cppunit; + * class SampleTest : public TestCase + * { + * public: + * SampleTest() {} + * ~SampleTest() {} + * void setUp() + * { // 各テストケース実行前に実施する処理を記載する。 + * } + * void tearDown() + * { // 各テストケース実行後に実施する処理を記載する。 + * } + * void testSample1() + * { // テスト内容を記載する。 + * } + * void testSample2() + * { // テスト内容を記載する。 + * } + * void suite() + * { + * RUN_TEST("テストサンプル1", testSample1); + * RUN_TEST("テストサンプル2", testSample2); + * } + * } + * + * --- + * int main() + * { + * SampleTest test; + * test.suite(); + * } + * @endcode + */ + class TestCase + { + public: + TestCase() noexcept; + TestCase(const TestCase &) noexcept = delete; + TestCase(TestCase &&) noexcept = delete; + TestCase &operator=(const TestCase &) noexcept = delete; + TestCase &operator=(TestCase &&) noexcept = delete; + virtual ~TestCase(); + virtual void setUp(); + virtual void tearDown(); + virtual void suite() = 0; + }; + + class TestManager + { + public: + TestManager(); + TestManager(const TestManager &) noexcept = delete; + TestManager(TestManager &&) noexcept = delete; + TestManager &operator=(const TestManager &) noexcept = delete; + TestManager &operator=(TestManager &&) noexcept = delete; + virtual ~TestManager(); + void addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e = nullptr); + void printResult(); + + private: + int okCount; + int ngCount; + }; + + extern TestManager testManager; + +/** + * テスト実行用マクロ + */ +#define RUN_TEST(title, func) \ + { \ + setUp(); \ + try \ + { \ + func(); \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, true); \ + } \ + catch (j::lang::AssertionError & e) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false, &e); \ + } \ + catch (...) \ + { \ + j::cppunit::testManager.addTestResult(title, #func, __FILE__, __LINE__, false); \ + } \ + tearDown(); \ + } + + } // cppunit +} // namespace j + +#endif // J_CPPUNIT_CPPUNIT_HPP diff --git a/include/j/io/term.hpp b/include/j/io/term.hpp new file mode 100644 index 0000000..1ac9471 --- /dev/null +++ b/include/j/io/term.hpp @@ -0,0 +1,104 @@ +/** + * @file term.hpp + * @brief 端末制御ヘッダファイル。 + * @copyright 20177777773 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_IO_TERM_HPP +#define J_IO_TERM_HPP + +#include +#include + +// ASCII エスケープコードによる表示 +namespace j +{ + namespace io + { + namespace Term + { + // 装飾 + constexpr std::string_view CLR = "\x1b[0m"; //!< 装飾無し + constexpr std::string_view BLD = "\x1b[1m"; //!< 太字 + constexpr std::string_view LGT = "\x1b[2m"; //!< 細字 + constexpr std::string_view ITA = "\x1b[3m"; //!< イタリック + constexpr std::string_view UND = "\x1b[4m"; //!< 下線 + constexpr std::string_view BLN = "\x1b[5m"; //!< 点滅 + constexpr std::string_view FBL = "\x1b[6m"; //!< 高速点滅 + constexpr std::string_view INV = "\x1b[7m"; //!< 反転表示 + constexpr std::string_view HID = "\x1b[8m"; //!< 非表示 (コピーは可能) + constexpr std::string_view CAN = "\x1b[9m"; //!< 取り消し + + // 文字色 + namespace Color + { + constexpr std::string_view DEF = "\x1b[39m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[30m"; //!< 黒 + constexpr std::string_view RED = "\x1b[31m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[32m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[33m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[34m"; //!< 青 + constexpr std::string_view MAG = "\x1b[35m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[36m"; //!< 水 + constexpr std::string_view WHT = "\x1b[37m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[90m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[91m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[92m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[93m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[94m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[95m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[96m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[97m"; //!< 白(高輝度) + } + + // 背景色 + namespace BgColor + { + constexpr std::string_view DEF = "\x1b[49m"; //!< デフォルト + constexpr std::string_view BLK = "\x1b[40m"; //!< 黒 + constexpr std::string_view RED = "\x1b[41m"; //!< 赤 + constexpr std::string_view GRN = "\x1b[42m"; //!< 緑 + constexpr std::string_view YEL = "\x1b[43m"; //!< 黄 + constexpr std::string_view BLU = "\x1b[44m"; //!< 青 + constexpr std::string_view MAG = "\x1b[45m"; //!< 紫 + constexpr std::string_view CYN = "\x1b[46m"; //!< 水 + constexpr std::string_view WHT = "\x1b[47m"; //!< 白 + constexpr std::string_view H_BLK = "\x1b[100m"; //!< 黒(高輝度) + constexpr std::string_view H_RED = "\x1b[101m"; //!< 赤(高輝度) + constexpr std::string_view H_GRN = "\x1b[102m"; //!< 緑(高輝度) + constexpr std::string_view H_YEL = "\x1b[103m"; //!< 黄(高輝度) + constexpr std::string_view H_BLU = "\x1b[104m"; //!< 青(高輝度) + constexpr std::string_view H_MAG = "\x1b[105m"; //!< 紫(高輝度) + constexpr std::string_view H_CYN = "\x1b[106m"; //!< 水(高輝度) + constexpr std::string_view H_WHT = "\x1b[107m"; //!< 白(高輝度) + } + + // カーソル移動 + namespace Cursor + { + constexpr std::string_view DEL_AFT = "\x1b[0J"; //!< カーソル以降を消去 + constexpr std::string_view DEL_BEF = "\x1b[1J"; //!< カーソル以前を消去 + constexpr std::string_view DEL = "\x1b[2J"; //!< 全体を消去 + constexpr std::string_view DEL_AFT_LINE = "\x1b[0K"; //!< カーソル行のカーソル以降を消去 + constexpr std::string_view DEL_BEF_LINE = "\x1b[1K"; //!< カーソル行のカーソル以前を消去 + constexpr std::string_view DEL_LINE = "\x1b[2K"; //!< カーソル行を消去 + + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim = nullptr, int m = 0); + + inline std::string_view UP(int n) { return MAKE_ESC_SEQ("A", n); } //!< n 上に移動 + inline std::string_view DOWN(int n) { return MAKE_ESC_SEQ("B", n); } //!< n 下に移動 + inline std::string_view RIGHT(int n) { return MAKE_ESC_SEQ("C", n); } //!< n 右に移動 + inline std::string_view LEFT(int n) { return MAKE_ESC_SEQ("D", n); } //!< n 左に移動 + inline std::string_view DOWN_LINE(int n) { return MAKE_ESC_SEQ("E", n); } //!< n 行下に移動(行頭[1列目]に移動) + inline std::string_view UP_LINE(int n) { return MAKE_ESC_SEQ("F", n); } //!< n 行上に移動(行頭[1列目]に移動) + inline std::string_view COL(int n) { return MAKE_ESC_SEQ("G", n); } //!< n 列に移動 + inline std::string_view MOVE(int n, int m) { return MAKE_ESC_SEQ("H", n, ";", m); } //!< n 行, m 列に移動 + inline std::string_view SCROLL(int n) { return MAKE_ESC_SEQ("S", n); } //!< n 行分次にスクロール + inline std::string_view SCROLL_R(int n) { return MAKE_ESC_SEQ("T", n); } //!< n 行分前にスクロール + } + } + } +} + +#endif // J_IO_TERM_HPP diff --git a/include/j/lang/assertion_error.hpp b/include/j/lang/assertion_error.hpp index cd76829..ac50330 100644 --- a/include/j/lang/assertion_error.hpp +++ b/include/j/lang/assertion_error.hpp @@ -24,6 +24,9 @@ // コンストラクタ AssertionError(const String &msg) noexcept; + // コンストラクタ + AssertionError(const String &msg, const char *file, const char *func, int line) noexcept; + // コピーコンストラクタ AssertionError(const AssertionError &t) noexcept; @@ -32,6 +35,23 @@ // デストラクタ ~AssertionError() noexcept; + + // ファイル名取得 + const char *getFile() const noexcept; + + // 関数名取得 + const char *getFunc() const noexcept; + + // 行番号取得 + int getLine() const noexcept; + + // 文字列表現取得 + String toString() const noexcept override; + + private: + const char *file; + const char *func; + int line; }; } // namespace lang diff --git a/include/j/lang/env.hpp b/include/j/lang/env.hpp new file mode 100644 index 0000000..f5c0ce9 --- /dev/null +++ b/include/j/lang/env.hpp @@ -0,0 +1,33 @@ +/** + * @file env.hpp + * @brief J Library Env ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_ENV_HPP +#define J_LANG_ENV_HPP + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + // 環境変数を取得 + String get(const String &name); + + // 環境変数を設定 + bool set(const String &name, const String &value, bool overwrite = true); + + // 環境変数を削除 + bool unset(const String &name); + + } // namespace Env + } // namespace lang +} // namespace j + +#endif // J_LANG_ENV_HPP \ No newline at end of file diff --git a/include/j/lang/object.hpp b/include/j/lang/object.hpp index ae42bcc..cb0871b 100644 --- a/include/j/lang/object.hpp +++ b/include/j/lang/object.hpp @@ -46,6 +46,12 @@ // ムーブ代入演算子 Object &operator=(Object &&obj) noexcept; + // 比較演算子 + bool operator==(const Object &obj) const noexcept; + + // 比較演算子(不等価演算子) + bool operator!=(const Object &obj) const noexcept; + // クラス名取得 virtual String getClassName() const noexcept; @@ -81,7 +87,10 @@ virtual std::unique_ptr clone() const noexcept; private: + // mutable をつけて、const メンバでも、mtx だけは変更を許可する。 + // 言語仕様的にいかがなものかとは思うが。。。 mutable std::mutex mtx; + std::condition_variable cv; }; diff --git a/include/j/lang/os.hpp b/include/j/lang/os.hpp deleted file mode 100644 index e1da1f5..0000000 --- a/include/j/lang/os.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file j_os.hpp - * @brief J Library OS 関連ヘッダファイル。 - * @copyright 2001 - 2024 Nomura Kei - * @depends - * - * Windows の場合、よく利用するヘッダをインクルードします。 - */ -#ifndef J_LANG_OS_HPP -#define J_LANG_OS_HPP -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) -#define IS_WINDOWS (1) - -// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 -// 詳細は、下記URL参照 -// http://www.digitalmars.com/d/archives/c++/idde/326.html -#ifdef __DMC__ -#define _WINSOCKAPI_ -#include -#endif - -// サポートする OS バージョン指定として、Windows 10 以降を指定する。 -// 参考までに他バージョンの値は次の通り。 -// Windows 2000 0x05000 -// Windows XP 0x0501 -// Windows Server 2003 0x0502 -// Windows Server 2008 0x0600 -// Windows 7 0x0601 -// Windows 8 0x0602 -// Windows 10 0x0A00 -#ifndef WINVER -#define WINVER 0x0A00 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0A00 -#endif - -// よく利用されるヘッダファイルをインクルードする -#include -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "iphlpapi.lib") -#endif - -#else -// ##### Windows 以外 ##### -#define IS_WINDOWS (0) - -#endif // Windows 判定 -#endif // J_LANG_OS_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 4f1cc08..9c35f6b 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -22,9 +23,12 @@ class String final : public Object { public: - // デフォルトコンストラクタ + // コンストラクタ String(const char *str = "") noexcept; + // コンストラクタ + String(const std::string &str) noexcept; + // コピーコンストラクタ String(const String &str) noexcept; @@ -85,6 +89,14 @@ // trim String trim() const noexcept; + // 検索 + int indexOf(int ch, int fromIndex = 0) const noexcept; + int indexOf(const String &str, int fromIndex = 0) const noexcept; + int lastIndexOf(int ch) const noexcept; + int lastIndexOf(const String &str) const noexcept; + int lastIndexOf(int ch, int fromIndex) const noexcept; + int lastIndexOf(const String &str, int fromIndex) const noexcept; + // 文字列表現取得 String toString() const noexcept override; diff --git a/include/j/lang/system.hpp b/include/j/lang/system.hpp index 826cd28..66a6090 100644 --- a/include/j/lang/system.hpp +++ b/include/j/lang/system.hpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace j { @@ -21,6 +22,15 @@ { // ライブラリロード // @see j/lang/Dl + // Dl loadLibrary(const String &libname); + // 環境変数取得 + // String getenv(const String &name); + // long currentTimeMillis() + // exit(int status); + // Map getenv(); + // Properties getProperties + // String getProperty(const String& key, const String& def = String()) + // String setProperty(const String& key, const String& value); } // namespace System } // namespace lang diff --git a/include/j/memory.hpp b/include/j/memory.hpp new file mode 100644 index 0000000..5d68e5b --- /dev/null +++ b/include/j/memory.hpp @@ -0,0 +1,104 @@ +/** + * @file memory.hpp + * @brief J Library Memory ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_HPP +#define J_MEMORY_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace j +{ + + /** + * メモリ管理。メモリの確保、解放を行います。 + * new 演算子呼び出し時のファイル名、行番号、関数名はスレッドセーフではありません。 + */ + namespace MemoryManager + { + extern thread_local const char *gFile; + extern thread_local const char *gFunc; + extern thread_local int gLine; + + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg); + + void *malloc(std::size_t size, const char *file, const char *func, int line); + void *aligned_alloc(std::size_t alignment, 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, const char *file, const char *func, int line); + + void *raw_malloc(std::size_t size); + void *raw_aligned_alloc(std::size_t alignment, std::size_t size); + void *raw_calloc(std::size_t nmemb, std::size_t size); + void *raw_realloc(void *ptr, std::size_t size); + void raw_free(void *ptr); + + extern DefaultMemoryListener listener; + }; + +} // namespace j + +#if defined(J_MEMORY_ENABLED) && (J_MEMORY_ENABLED) + +// メモリ管理有効 +void *operator new(std::size_t size); +void *operator new(std::size_t size, std::align_val_t al); +void *operator new(std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void *operator new[](std::size_t size); +void *operator new[](std::size_t size, std::align_val_t al); +void *operator new[](std::size_t size, const std::nothrow_t &t) noexcept; +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &t) noexcept; + +void operator delete(void *p) noexcept; +void operator delete(void *p, std::align_val_t al) noexcept; +void operator delete(void *p, std::size_t sz) noexcept; +void operator delete(void *p, std::size_t sz, std::align_val_t al) noexcept; + +void operator delete[](void *p) noexcept; +void operator delete[](void *p, std::align_val_t al) noexcept; +void operator delete[](void *p, std::size_t sz) noexcept; +void operator delete[](void *p, std::size_t sz, std::align_val_t al) noexcept; + +#if !defined(J_MEMORY_RAW) +#define new ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? nullptr \ + : new + +#define delete ( \ + j::MemoryManager::gFile = __FILE__, \ + j::MemoryManager::gFunc = __func__, \ + j::MemoryManager::gLine = __LINE__, \ + false) \ + ? void() \ + : delete + +#define malloc(size) j::MemoryManager::malloc(size, __FILE__, __func__, __LINE__) +#define aligned_alloc(alignment, size) j::MemoryManager::aligned_alloc(alignment, size, __FILE__, __func__, __LINE__) +#define calloc(nmemb, size) j::MemoryManager::calloc(nmemb, size, __FILE__, __func__, __LINE__) +#define realloc(ptr, size) j::MemoryManager::realloc(ptr, size, __FILE__, __func__, __LINE__) +#define free(ptr) j::MemoryManager::free(ptr, __FILE__, __func__, __LINE__) +#endif // !defined(J_MEMORY_RAW) + +#else +#include +#endif + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_entry.hpp b/include/j/memory_entry.hpp new file mode 100644 index 0000000..bfc342f --- /dev/null +++ b/include/j/memory_entry.hpp @@ -0,0 +1,49 @@ +/** + * @file memory_entry.hpp + * @brief J Library Memory Entry ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_mark.hpp + */ +#ifndef J_MEMORY_ENTRY_HPP +#define J_MEMORY_ENTRY_HPP + +#include + +#include +#include + +namespace j +{ + + /** + * メモリエントリ。 + */ + struct MemoryEntry + { + int size; //!< メモリサイズ + MemoryMark mark; //!< メモリ状態 + const char *file; //!< メモリ確保ファイル名 + const char *func; //!< メモリ確保関数名 + int line; //!< メモリ確保行番号 + MemoryEntry *_prev; //!< 前の管理メモリポインタ + MemoryEntry *_next; //!< 次の管理メモリポインタ + void *_padding[2]; //!< パディング + void *_data; //!< データ + + // メモリエントリ情報出力 + void dump(std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl = false) const; + + private: + // カラム数取得 + int getColumn(int bytes, bool binary, bool ascii, int maxColumn) const; + void printBase(std::ostream &os, int column) const; + void printBinary(std::ostream &os, int bytes) const; + void printAscii(std::ostream &os, int bytes) const; + const char *getSizeStr() const; + }; + +} // namespace j + +#endif // J_MEMORY_ENTRY_HPP diff --git a/include/j/memory_listener.hpp b/include/j/memory_listener.hpp new file mode 100644 index 0000000..c6d58a8 --- /dev/null +++ b/include/j/memory_listener.hpp @@ -0,0 +1,99 @@ +/** + * @file memory_listener.hpp + * @brief J Library Memory Listener ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + * j/memory_entrry.hpp + */ +#ifndef J_MEMORY_LISTENER_HPP +#define J_MEMORY_LISTENER_HPP + +#include +#include + +namespace j +{ + /** + * メモリが確保、解放、あるいは確保/解放時にエラーが発生した際のリスナインタフェース。 + * ユーザは、本リスナを実装し、MemoryManager::setListener メソッドにて登録することで、 + * メモリ確保、解放、エラー発生時の通知を受信可能です。 + */ + class MemoryListener + { + public: + /** + * デストラクタ + */ + virtual ~MemoryListener() = default; + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry 確保したメモリの情報 + */ + virtual void alloc(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ解放時に呼び出されます。 + * entry には、メモリを確保した場所の情報が格納されています。 + * + * @param entry 解放するメモリの情報 + */ + virtual void free(const MemoryEntry &entry) noexcept = 0; + + /** + * メモリ確保/解放失敗時に呼び出されます。 + * + * @param entry エラー発生における情報 + * @param msg エラーメッセージ + */ + virtual void error(const MemoryEntry &entry, const char *msg) noexcept = 0; + }; + + /** + * メモリ確保、解放、エラー発生時の通知を受け取るデフォルトのクラスです。 + */ + class DefaultMemoryListener : public MemoryListener + { + public: + // コンストラクタ + DefaultMemoryListener() noexcept; + + // コピーコンストラクタ + DefaultMemoryListener(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブコンストラクタ + DefaultMemoryListener(DefaultMemoryListener &&obj) noexcept = delete; + + // デストラクタ + virtual ~DefaultMemoryListener() noexcept; + + // コピー代入演算子 + DefaultMemoryListener &operator=(const DefaultMemoryListener &obj) noexcept = delete; + + // ムーブ代入演算子 + DefaultMemoryListener &operator=(DefaultMemoryListener &&obj) noexcept = delete; + + // 設定 + void config(bool isPrint = false, bool isPrintError = true, int dumpBytes = 16, int maxColumn = 130) noexcept; + + // メモリ確保時に呼び出されるメソッド + virtual void alloc(const MemoryEntry &entry) noexcept; + + // メモリ解放時に呼び出されるメソッド + virtual void free(const MemoryEntry &entry) noexcept; + + // エラー発生時に呼び出されるメソッド + virtual void error(const MemoryEntry &entry, const char *msg) noexcept; + + private: + bool isPrint; + bool isPrintError; + int dumpBytes; + int maxColumn; + }; + +} // namespace j + +#endif // J_MEMORY_HPP diff --git a/include/j/memory_mark.hpp b/include/j/memory_mark.hpp new file mode 100644 index 0000000..a7a0486 --- /dev/null +++ b/include/j/memory_mark.hpp @@ -0,0 +1,36 @@ +/** + * @file memory_mark.hpp + * @brief J Library Memory Mark ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_MEMORY_MARK_HPP +#define J_MEMORY_MARK_HPP + +#include + +namespace j +{ + + /** + * メモリ状態 + */ + enum MemoryMark + { + DELETED = 0x55AA0000, //!< 解放済みメモリ + ALLOCATED = 0x55AA1111, //!< 確保済みメモリ + ALLOCATED_ALIGNED = 0x55AA11FF, //!< 確保済みメモリ(aligned) + ALLOCATED_NEW = 0x55AA2222, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ALIGNED = 0x55AA22FF, //!< 確保済みメモリ(new) + ALLOCATED_NEW_ARRAY = 0x55AA4444, //!< 確保済みメモリ(new[]) + ALLOCATED_NEW_ARRAY_ALIGNED = 0x55AA44FF //!< 確保済みメモリ(new[]) + }; + +#define IS_MANAGED_MEMORY(mark) ((mark & 0xFFFF0000) == 0x55AA0000) +#define IS_MANAGED_ALIGNED(mark) ((mark & 0xFFFF00FF) == 0x55AA00FF) +#define TO_NO_ALIGNED(mark) (((mark | 0x0000FF00) >> 8) | (mark & 0xFFFFFF00)) + +} // namespace j + +#endif // J_MEMORY_MARK_HPP diff --git a/include/j/os.hpp b/include/j/os.hpp new file mode 100644 index 0000000..a21bedb --- /dev/null +++ b/include/j/os.hpp @@ -0,0 +1,53 @@ +/** + * @file os.hpp + * @brief J Library OS 関連ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * + * Windows の場合、よく利用するヘッダをインクルードします。 + */ +#ifndef J_OS_HPP +#define J_OS_HPP +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) +#define IS_WINDOWS (1) + +// DMC にて winsoc2.h を利用する場合、_WINSOCK_API_ が必要 +// 詳細は、下記URL参照 +// http://www.digitalmars.com/d/archives/c++/idde/326.html +#ifdef __DMC__ +#define _WINSOCKAPI_ +#include +#endif + +// サポートする OS バージョン指定として、Windows 10 以降を指定する。 +// 参考までに他バージョンの値は次の通り。 +// Windows 2000 0x05000 +// Windows XP 0x0501 +// Windows Server 2003 0x0502 +// Windows Server 2008 0x0600 +// Windows 7 0x0601 +// Windows 8 0x0602 +// Windows 10 0x0A00 +#ifndef WINVER +#define WINVER 0x0A00 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +// よく利用されるヘッダファイルをインクルードする +#include +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "iphlpapi.lib") +#endif + +#else +// ##### Windows 以外 ##### +#define IS_WINDOWS (0) + +#endif // Windows 判定 +#endif // J_OS_HPP diff --git a/include/j/overload.hpp b/include/j/overload.hpp new file mode 100644 index 0000000..bba9aff --- /dev/null +++ b/include/j/overload.hpp @@ -0,0 +1,72 @@ +/** + * @file overload.hpp + * @brief J Library Overload ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + */ +#ifndef J_OVERLOAD_HPP +#define J_OVERLOAD_HPP + +/** + * 関数のオーバーロードを実現します。 + * テンプレートの特殊化等の際に利用可能です。 + * 引数の数に応じて、「指定された関数名(のプレフィックス) + 引数の数」の関数が実行されます。 + * + * @code + * 例) add + * #define add(...) J_OVERLOAD(add_, __VA_ARGS__) + * + * 引数が2つの場合、add_2 の関数が呼び出されます。 + * 引数が3つの場合、add_3 の関数が呼び出されます。 + * : + * 引数が9つの場合、add_9 の関数が呼び出されます。 + * + * + * @endcode + * + * @param func オーバーロードする関数のベース名 + * @param ... __VA_ARGS__ + * @return 引数に応じて呼び出す関数 + */ +#define J_OVERLOAD(func, ...) J_OVERLOAD_SUB(func, J_ARGS_LENGTH(__VA_ARGS__))(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// 以下、オーバーロード実現のためのマクロ群 +// + +/** + * 関数名を生成します。 + * 関数名ベース+引数の数の関数名を生成します。 + * + * 例) + * 関数名ベース add_ + * 引数の数 1 + * + * add_1 + * + * @param func 関数名のベース + * @param args_length 引数の数 + * @return 引数名 + */ +#define J_OVERLOAD_SUB(func, args_length) J_STRCAT(func, args_length) + +/** + * 可変長引数の個数を返します。 + * + * @param ... 可変長引数 + * @return 引数の数 + */ +#define J_ARGS_LENGTH(...) J_ARGS_LENGTH_SUB(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#define J_ARGS_LENGTH_SUB(v1, v2, v3, v4, v5, v6, v7, v8, v9, LENGTH, ...) LENGTH + +/** + * 指定された文字列を連結します。 + * + * @param s1 文字列1 + * @param s2 文字列2 + * @return 連結した文字列 + */ +#define J_STRCAT(s1, s2) s1##s2 + +#endif // J_OVERLOAD_HPP diff --git a/j/Makefile b/j/Makefile deleted file mode 100644 index b2cf1d5..0000000 --- a/j/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= .. -RULEDIR ?= $(TOPDIR)/mk -NAME = -TARGET = $(NAME) -SUBDIRS = lang -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/Makefile b/j/lang/Makefile deleted file mode 100644 index 1da5e45..0000000 --- a/j/lang/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj -TARGET = $(NAME) -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/j/lang/src/assertion_error.cpp b/j/lang/src/assertion_error.cpp deleted file mode 100644 index 663ff73..0000000 --- a/j/lang/src/assertion_error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * AssertionError を構築します。 - */ - AssertionError::AssertionError() noexcept : Error() - { - // NOP - } - - /** - * AssertionError を構築します。 - * - * @param msg メッセージ - */ - AssertionError::AssertionError(const String &msg) noexcept : Error(msg) - { - // NOP - } - - /** - * AssertionError のコピーコンストラクタ。 - * - * @param t コピー元 AssertionError - */ - AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t) - { - // NOP - } - - /** - * AssertionError のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - AssertionError::AssertionError(AssertionError &&t) noexcept : Error(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - AssertionError::~AssertionError() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/dl.cpp b/j/lang/src/dl.cpp deleted file mode 100644 index c95ce33..0000000 --- a/j/lang/src/dl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include - -#if (!IS_WINDOWS) -#include -#endif - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Dl を構築します。 - */ - Dl::Dl(const String &fileName) noexcept - { -#if (IS_WINDOWS) - handle = ::LoadLibraryEx(fileName); -#else - handle = ::dlopen(fileName, RTLD_LAZY); -#endif - // TODO : handle == 0 の場合エラー - } - - /** - * Dl のコピーコンストラクタ。 - * - * @param dl コピー元オブジェクト - */ - Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) - { /* NOP */ - } - - /** - * Dl のムーブコンストラクタ。 - * - * @param dl ムーブ元オブジェクト - */ - Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) - { /* NOP */ - } - - // デストラクタ - Dl::~Dl() noexcept - { -#if (IS_WINDOWS) - ::FreeLibrary(handle); -#else - ::dlclose(handle); -#endif - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param dl コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(const Dl &dl) noexcept - { - if (this != &dl) - { - Object::operator=(dl); - handle = dl.handle; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param dl ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Dl &Dl::operator=(Dl &&dl) noexcept - { - if (this != &dl) - { - Object::operator=(std::move(dl)); - handle = std::move(dl.handle); - handle = nullptr; - } - return *this; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Dl::toString() const noexcept - { - return Object::toString(); - } - - /** - * 指定されたシンボルがロードされたメモリのアドレスを返します。 - * - * @code - * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); - * Dl dl("kernel32.dll"); - * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); - * void* buffer[64]; - * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); - * for (int i = 0; i < cnt; i++) - * { - * std::cout << buffer[i] << std::endl; - * } - * @endcode - * - * @param symbol シンボル名 - * @return シンボルのアドレス - */ - dl_func_t Dl::sym(const String &symbol) - { -#if (IS_WINDOWS) - return ::GetProcAddress(handle, symbol); -#else - return ::dlsym(handle, symbol); -#endif - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/errno.cpp b/j/lang/src/errno.cpp deleted file mode 100644 index 510625e..0000000 --- a/j/lang/src/errno.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include - -#include - -namespace j -{ - namespace lang - { - namespace Errno - { -#if (IS_WINDOWS) - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows - // - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - SetLastError(errnum); - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return GetLastError(); - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - LPVOID lpMsgBuf; - int ret = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ - 0, // メッセージ定義位置 - errnum, // エラーコード - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID - (LPSTR)&lpMsgBuf, // バッファアドレス - 0, // バッファサイズ - 0); // 挿入句 - - if (ret != 0) - { - String msg((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - return msg; - } - else - { - String msg(); - return msg; - } - } -#else - //////////////////////////////////////////////////////////////////////////////// - // - // For Windows 以外 - // - - /** - * エラー番号を設定します。 - * - * @param errnum エラー番号 - */ - void set(int errnum) - { - errno = errnum; - } - - /** - * エラー番号を取得します。 - * - * @return エラー番号 - */ - int get() - { - return errno; - } - - /** - * エラー番号に対応するエラーメッセージを取得します。 - * - * @param errnum エラー番号 - * @return エラーメッセージ - */ - String message(int errnum) - { - String msg(strerror(errnum)); - return msg; - } -#endif // IS_WINDOWS - - } // namespace Errno - } // namespace lang -} // namespace j diff --git a/j/lang/src/error.cpp b/j/lang/src/error.cpp deleted file mode 100644 index bf8dacb..0000000 --- a/j/lang/src/error.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Error を構築します。 - */ - Error::Error() noexcept : Throwable() - { - // NOP - } - - /** - * Error を構築します。 - * - * @param msg メッセージ - */ - Error::Error(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Error のコピーコンストラクタ。 - * - * @param t コピー元 Error - */ - Error::Error(const Error &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Error のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Error::Error(Error &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Error::~Error() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/exception.cpp b/j/lang/src/exception.cpp deleted file mode 100644 index 98aafb5..0000000 --- a/j/lang/src/exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * Exception を構築します。 - */ - Exception::Exception() noexcept : Throwable() - { - // NOP - } - - /** - * Exception を構築します。 - * - * @param msg メッセージ - */ - Exception::Exception(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * Exception のコピーコンストラクタ。 - * - * @param t コピー元 Exception - */ - Exception::Exception(const Exception &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * Exception のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - Exception::~Exception() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/illegal_argument_exception.cpp b/j/lang/src/illegal_argument_exception.cpp deleted file mode 100644 index 445f866..0000000 --- a/j/lang/src/illegal_argument_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IllegalArgumentException を構築します。 - */ - IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IllegalArgumentException を構築します。 - * - * @param msg メッセージ - */ - IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IllegalArgumentException のコピーコンストラクタ。 - * - * @param t コピー元 IllegalArgumentException - */ - IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IllegalArgumentException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IllegalArgumentException::~IllegalArgumentException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/index_out_of_bounds_exception.cpp b/j/lang/src/index_out_of_bounds_exception.cpp deleted file mode 100644 index eabe527..0000000 --- a/j/lang/src/index_out_of_bounds_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * IndexOutOfBoundsException を構築します。 - */ - IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() - { - // NOP - } - - /** - * IndexOutOfBoundsException を構築します。 - * - * @param msg メッセージ - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * IndexOutOfBoundsException のコピーコンストラクタ。 - * - * @param t コピー元 IndexOutOfBoundsException - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * IndexOutOfBoundsException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/main.cpp b/j/lang/src/main.cpp deleted file mode 100644 index 928dc08..0000000 --- a/j/lang/src/main.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -using namespace j; -using namespace j::lang; - -class X : public Object -{ -public: - String toString() const noexcept override - { - String str("This is X"); - return str; - } -}; - -int main(int, char **) -{ - /* - String str = "AbcdefAbaBcdefGhI"; - bool ret = str.startsWith("Abc"); - std::cout << "startsWith:[ok] " << ret << std::endl; - - ret = str.startsWith("Abd"); - std::cout << "startsWith:[ng] " << ret << std::endl; - - ret = str.endsWith("GhI"); - std::cout << "endsWith:[ok] " << ret << std::endl; - - ret = str.endsWith("xGhi"); - std::cout << "endsWith:[ng] " << ret << std::endl; - - std::cout << str.toLowerCase() << std::endl; - std::cout << str.toUpperCase() << std::endl; - - String str2 = " a a daf\t"; - std::cout << str2 << std::endl; - std::cout << str2.trim() << std::endl; - - std::cout << str << std::endl; - std::cout << str.replace('A', '-') << std::endl; - std::cout << str.replace("Ab", "--") << std::endl; - std::cout << str.replaceAll("Ab", "--") << std::endl; - */ - String t1 = "Hello"; - String t2 = "World"; - String t3 = t1 + " " + t2; - for (int i = 0; i < 100; i++) - { - t3 += "!"; - } - - std::cout << t3 << std::endl; - - std::cout << t1.equals(t2) << std::endl; - - String t4 = "World"; - String t5 = t2; - std::cout << "t2:hash=" << t2.hashCode() << std::endl; - std::cout << "t4:hash=" << t4.hashCode() << std::endl; - std::cout << t2.equals(t4) << std::endl; - std::cout << t5.equals(t2) << std::endl; - - Errno::set(EINVAL); - Throwable tt1; - Throwable tt2("MSG"); - Throwable tt3 = tt1; - Object *tt4 = &tt1; - - std::cout << tt1 << std::endl; - std::cout << tt2 << std::endl; - std::cout << tt1.equals(tt2) << std::endl; - std::cout << tt1.equals(tt3) << std::endl; - std::cout << "tt1 == tt4 : " << tt1.equals(*tt4) << std::endl; - std::cout << "tt4 == tt1 : " << tt4->equals(tt1) << std::endl; - - String str4 = tt3.getMessage(); - std::cout << str4 << std::endl; - - Error err("Error"); - Error err2 = err; - std::cout << err2 << std::endl; - return 0; -} diff --git a/j/lang/src/object.cpp b/j/lang/src/object.cpp deleted file mode 100644 index ca0772e..0000000 --- a/j/lang/src/object.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Object を構築します。 - */ - Object::Object() noexcept { /* NOP */ } - - /** - * Object のコピーコンストラクタ。 - * - * @param obj コピー元オブジェクト - */ - Object::Object(const Object &) noexcept { /* NOP */ } - - /** - * Object のムーブコンストラクタ。 - * - * @param obj ムーブ元オブジェクト - */ - Object::Object(Object &&) noexcept { /* NOP */ } - - // デストラクタ - // virtual Object::~Object() noexcept = default; - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param obj コピー元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(const Object &) noexcept - { // 特にコピーする要素はない。 - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Object &Object::operator=(Object &&) noexcept - { // 特に移すものはない。 - return *this; - } - - /** - * クラス名を取得します。 - * - * @return クラス名 - */ - String Object::getClassName() const noexcept - { - String str(typeid(*this).name()); - return str; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Object::toString() const noexcept - { - // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); - return getClassName(); - } - - /** - * 指定されたオブジェクトが同じクラスか否かを返します。 - * - * @param obj オブジェクト - * @return true/false (同じクラス/異なるクラス) - */ - bool Object::isSameClass(const Object &obj) const noexcept - { - return (typeid(*this) == typeid(obj)); - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool Object::equals(const Object &obj) const noexcept - { - if (isSameClass(obj)) - { // 同じクラス - int ownHash = hashCode(); - int objHash = obj.hashCode(); - if (ownHash == objHash) - { // ハッシュコードが一緒 - String ownStr = toString(); - String objStr = obj.toString(); - return ownStr.equals(objStr); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int Object::hashCode() const noexcept - { - return (reinterpret_cast(this)); - } - - /** - * 待機中のスレッドを再開します。 - */ - void Object::notify() - { - std::lock_guard lock(mtx); - cv.notify_one(); - } - - /** - * 待機中のすべてのスレッドを再開します。 - */ - void Object::notifyAll() - { - std::lock_guard lock(mtx); - cv.notify_all(); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait() - { - std::unique_lock lock(mtx); - cv.wait(lock); - } - - /** - * 現在のスレッドを待機させます。 - */ - void Object::wait(int msec) - { - std::unique_lock lock(mtx); - cv.wait_for(lock, std::chrono::milliseconds(msec)); - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Object::clone() const noexcept - { - return std::make_unique(*this); - } - - /** - * 出力用 - * - * @param os output stream - * @param obj オブジェクト - */ - std::ostream &operator<<(std::ostream &os, const Object &obj) - { - os << obj.toString(); - return os; - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/runtime_exception.cpp b/j/lang/src/runtime_exception.cpp deleted file mode 100644 index 16c609f..0000000 --- a/j/lang/src/runtime_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * RuntimeException を構築します。 - */ - RuntimeException::RuntimeException() noexcept : Throwable() - { - // NOP - } - - /** - * RuntimeException を構築します。 - * - * @param msg メッセージ - */ - RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) - { - // NOP - } - - /** - * RuntimeException のコピーコンストラクタ。 - * - * @param t コピー元 RuntimeException - */ - RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) - { - // NOP - } - - /** - * RuntimeException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - RuntimeException::~RuntimeException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/string.cpp b/j/lang/src/string.cpp deleted file mode 100644 index 6fa1ac4..0000000 --- a/j/lang/src/string.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include - -#include - -// 入力ストリーム用バッファサイズ -static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; - -namespace j -{ - namespace lang - { - // [補足] - // std::unique_ptr value; において、 - // インデックスに対する操作も多々あるため、value.get() + index ではなく、 - // 直観的にわかりやすい, &value[index] の表現にて実装している。 - - /** - * String を構築します。 - * - * @param str 文字列 - */ - String::String(const char *str) noexcept - { - setValue(str); - } - - /** - * String のコピーコンストラクタ。 - * - * @param str コピー元 String - */ - String::String(const String &str) noexcept : Object(str) - { - setValue(&str.value[0]); - } - - /** - * String のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) - { - str.value = nullptr; - str.len = 0; - } - - /** - * デストラクタ。 - */ - String::~String() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - String &String::operator=(const String &str) noexcept - { - if (this != &str) - { - Object::operator=(str); - setValue(&str.value[0]); - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 - if (this != &str) - { - Object::operator=(std::move(str)); - value = std::move(str.value); - len = str.len; - str.value = nullptr; - str.len = 0; - } - return *this; - } - - /** - * 指定された文字列を結合します。 - */ - String &String::operator+=(const String &str) noexcept - { - int newLen = len + str.len; - std::unique_ptr newStr = std::make_unique(newLen + 1); - std::strncpy(&newStr[0], &value[0], len); - std::strncpy(&newStr[len], &str.value[0], str.len); - newStr[newLen] = '\0'; - value = std::move(newStr); - len = newLen; - return *this; - } - - /** - * const char* 型に変換します。 - */ - String::operator const char *() const - { - return value.get(); - } - - /** - * 文字列の長さを返します。 - * - * @return 文字列の長さ - */ - int String::length() const noexcept - { - return len; - } - - /** - * 指定された位置の文字を返します。 - * - * @param index 位置 - * @return 文字 - */ - char String::charAt(int index) const - { - if ((index < 0) || (index >= len)) - { - // TODO: IndexOutOfBoundsException - } - return value[index]; - } - - /** - * 指定された部分文字列を返します。 - * - * @param beginIndex 開始位置 - * @param endIndex 終了位置 - * @return 部分文字列 - */ - String String::substring(int beginIndex, int endIndex) const - { - if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) - { - int subLen = endIndex - beginIndex; - std::unique_ptr subStr = std::make_unique(subLen + 1); - std::strncpy(&subStr[0], &value[beginIndex], subLen); - subStr[subLen] = '\0'; - String result(&subStr[0]); - return result; - } - else - { - // TODO: IndexOutOfBoundsException - return nullptr; - } - } - - /** - * 指定された文字列が含まれるか否かを返します。 - * - * @param str 文字列 - * @return true/false (含まれる/含まれない) - */ - bool String::contains(const String &str) const noexcept - { - return (std::strstr(&value[0], &str.value[0]) != nullptr); - } - - /** - * 指定された文字を置換します。 - * - * @param oldChar 置換前文字 - * @param newChar 置換後文字 - * @return 置換された文字列 - */ - String String::replace(char oldChar, char newChar) const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - if (str.value[idx] == oldChar) - { - str.value[idx] = newChar; - } - } - return str; - } - - // 文字列置換 - String String::replace(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); - String str(res.c_str()); - return str; - } - - // 文字列置換 - String String::replaceAll(const String ®ex, const String &replacement) const - { - std::regex re(®ex.value[0]); - std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_any); - String str(res.c_str()); - return str; - } - - // 分割 - std::unique_ptr String::split(const String &) const noexcept - { - return nullptr; - } - - // 先頭の文字列が一致するか - bool String::startsWith(const String &prefix) const noexcept - { - if (prefix.len > len) - { - return false; - } - for (int idx = 0; idx < prefix.len; idx++) - { - if (value[idx] != prefix.value[idx]) - { - return false; - } - } - return true; - } - - // 末尾の文字列が一致するか - bool String::endsWith(const String &suffix) const noexcept - { - if (suffix.len > len) - { - return false; - } - int value_idx = (len - suffix.len); - for (int idx = 0; idx < suffix.len; idx++) - { - if (value[value_idx] != suffix.value[idx]) - { - return false; - } - value_idx++; - } - return true; - } - - // 小文字変換 - String String::toLowerCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::tolower(str.value[idx]); - } - return str; - } - - // 大文字変換 - String String::toUpperCase() const noexcept - { - String str(*this); - for (int idx = 0; idx < len; idx++) - { - str.value[idx] = std::toupper(str.value[idx]); - } - return str; - } - - // trim - String String::trim() const noexcept - { - int beginIndex = 0; - for (; beginIndex < len; beginIndex++) - { - if (value[beginIndex] > 0x20) - { - break; - } - } - int endIndex = len; - for (; endIndex >= beginIndex; endIndex--) - { - if (value[endIndex] > 0x20) - { - break; - } - } - int trimedLen = endIndex - beginIndex; - std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); - std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); - trimedStr[trimedLen] = '\0'; - String result(&trimedStr[0]); - return result; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String String::toString() const noexcept - { - String str(*this); - return str; - } - - /** - * 指定されたオブジェクトと合致するか否かを返します。 - * - * @param obj 比較するオブジェクト - * @return true/false (合致する/しない) - */ - bool String::equals(const Object &obj) const noexcept - { - bool isSame = isSameClass(obj); - if (isSame) - { - const String &str = dynamic_cast(obj); - if (len == str.len) - { - return (std::strcmp(&value[0], &str.value[0]) == 0); - } - } - return false; - } - - /** - * ハッシュコードを取得します。 - * - * @return ハッシュコード - */ - int String::hashCode() const noexcept - { - int hash = 0; - for (int idx = 0; idx < len; idx++) - { - hash = 31 * hash + value[idx]; - } - return hash; - } - - /** - * 2つの文字列を結合します。 - * - * @param str1 文字列 - * @param str2 文字列 - * @return 結合後の文字列 - */ - String operator+(const String &str1, const String &str2) noexcept - { - String str = str1; - str += str2; - return str; - } - - /** - * 出力用 - * - * @param os output stream - * @param str 出力文字列 - * @return output stream - */ - std::ostream &operator<<(std::ostream &os, const String &str) - { - if (str.value != nullptr) - { - os << &str.value[0]; - } - return os; - } - - /** - * 入力用 - * - * @param is input stream - * @param str 入力先 String - * @return input stream - */ - std::istream &operator>>(std::istream &is, String &str) - { - char buff[MAX_ISTREAM_BUFFER_SIZE]; - is >> buff; - str = String(buff); - return is; - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 文字列データを設定します。 - * - * @param str 設定する文字列 - */ - void String::setValue(const char *str) - { - if (str) - { - len = std::strlen(str); - value = std::make_unique(len + 1); - std::strcpy(&value[0], str); - } - else - { - len = 0; - value = std::make_unique(1); - value[0] = '\0'; - } - } - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr String::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/throwable.cpp b/j/lang/src/throwable.cpp deleted file mode 100644 index e42b5d6..0000000 --- a/j/lang/src/throwable.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include - -#include -#include - -namespace j -{ - namespace lang - { - - /** - * Throwable を構築します。 - */ - Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) - { - // NOP - } - - /** - * Throwable を構築します。 - * - * @param msg メッセージ - */ - Throwable::Throwable(const String &msg) noexcept : message(msg) - { - // NOP - } - - /** - * Throwable のコピーコンストラクタ。 - * - * @param t コピー元 Throwable - */ - Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) - { - // NOP - } - - /** - * Throwable のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) - { - t.message = nullptr; - } - - /** - * デストラクタ。 - */ - Throwable::~Throwable() noexcept - { - // NOP - } - - /** - * コピー代入演算子。 - * コピーして代入します。 - * - * @param str コピー元 String - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(const Throwable &t) noexcept - { - if (this != &t) - { - Object::operator=(t); - message = t.message; - } - return *this; - } - - /** - * ムーブ代入演算子。 - * - * @param obj ムーブ元オブジェクト - * @return 本オブジェクトへの参照 - */ - Throwable &Throwable::operator=(Throwable &&t) noexcept - { - if (this != &t) - { - Object::operator=(std::move(t)); - message = std::move(t.message); - t.message = nullptr; - } - return *this; - } - - /** - * エラーメッセージを取得します。 - * - * @return エラーメッセージ - */ - String Throwable::getMessage() const noexcept - { - return message; - } - - /** - * 本オブジェクトの文字列表現を返します。 - * - * @return 本オブジェクトの文字列表現 - */ - String Throwable::toString() const noexcept - { - return getMessage(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // protected - // - - /** - * 本オブジェクトの複製を取得します。 - * - * [備考] - * 派生クラスが、ポリモーフィズムをサポートするために、 - * unique_ptr を返すようにしています。 - * - * @return 本オブジェクトの複製 - */ - std::unique_ptr Throwable::clone() const noexcept - { - return std::make_unique(*this); - } - - } // namespace lang -} // namespace j diff --git a/j/lang/src/unsupported_operation_exception.cpp b/j/lang/src/unsupported_operation_exception.cpp deleted file mode 100644 index 4a9dd54..0000000 --- a/j/lang/src/unsupported_operation_exception.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include - -namespace j -{ - namespace lang - { - /** - * UnsupportedOperationException を構築します。 - */ - UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() - { - // NOP - } - - /** - * UnsupportedOperationException を構築します。 - * - * @param msg メッセージ - */ - UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) - { - // NOP - } - - /** - * UnsupportedOperationException のコピーコンストラクタ。 - * - * @param t コピー元 UnsupportedOperationException - */ - UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) - { - // NOP - } - - /** - * UnsupportedOperationException のムーブコンストラクタ。 - * - * @param str ムーブ元 String - */ - UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) - { - // NOP - } - - /** - * デストラクタ。 - */ - UnsupportedOperationException::~UnsupportedOperationException() noexcept - { - // NOP - } - - } // namespace lang -} // namespace j diff --git a/lib/libcalc.so b/lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/lib/libcalc.so Binary files differ diff --git a/mk/base-conf.mk b/mk/base-conf.mk index 6997761..61848d2 100644 --- a/mk/base-conf.mk +++ b/mk/base-conf.mk @@ -20,6 +20,7 @@ # その他のフラグについては、gcc のヘルプを参照ください。 # ------------------------------------------------------------------------------ OPTIMIZATION = -O2 +#OPTIMIZATION = -O0 # ------------------------------------------------------------------------------ # ヘッダー依存関係出力 diff --git a/mk/check-lcov-rule.mk b/mk/check-lcov-rule.mk index b75e472..22b108a 100644 --- a/mk/check-lcov-rule.mk +++ b/mk/check-lcov-rule.mk @@ -9,7 +9,7 @@ ifeq ($(strip $(TARGET)),ut.exe) -@$(MKDIR) -p $(LCOV_REPORT_DIR) $(LCOV) -c -d $(OBJDIR) $(LCOV_FLAGS) -o $(LCOV_INFO) - $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" -o $(LCOV_INFO) + $(LCOV) $(LCOV_FLAGS) -r $(LCOV_INFO) "*/test/*" "*/c++/*" -o $(LCOV_INFO) $(GENHTML) $(GENHTML_FLAGS) -o $(LCOV_REPORT_DIR) $(LCOV_INFO) endif diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..8417549 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= .. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = j +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/Makefile b/modules/j/Makefile new file mode 100644 index 0000000..468c58c --- /dev/null +++ b/modules/j/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = +TARGET = $(NAME) +SUBDIRS = base +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/Makefile b/modules/j/base/Makefile new file mode 100644 index 0000000..45099e6 --- /dev/null +++ b/modules/j/base/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libjbase +TARGET = $(NAME).so +SUBDIRS = test +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/base/README.md b/modules/j/base/README.md new file mode 100644 index 0000000..97c84ee --- /dev/null +++ b/modules/j/base/README.md @@ -0,0 +1,9 @@ +* Model : j.base + +** includes +- j +- j/lang +- j/util +- j/io +- j/net + diff --git a/modules/j/base/src/assert.cpp b/modules/j/base/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/base/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/assertion_error.cpp b/modules/j/base/src/assertion_error.cpp new file mode 100644 index 0000000..f2f2520 --- /dev/null +++ b/modules/j/base/src/assertion_error.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +namespace j +{ + namespace lang + { + /** + * AssertionError を構築します。 + */ + AssertionError::AssertionError() noexcept : Error(), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg) noexcept : Error(msg), file(""), func(""), line(0) + { + // NOP + } + + /** + * AssertionError を構築します。 + * + * @param msg メッセージ + */ + AssertionError::AssertionError(const String &msg, const char *file, const char *func, int line) noexcept + : Error(msg), file(file), func(func), line(line) + { + // NOP + } + + /** + * AssertionError のコピーコンストラクタ。 + * + * @param t コピー元 AssertionError + */ + AssertionError::AssertionError(const AssertionError &t) noexcept : Error(t), file(t.file), func(t.func), line(t.line) + { + // NOP + } + + /** + * AssertionError のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + AssertionError::AssertionError(AssertionError &&t) noexcept + : Error(std::move(t)), file(std::move(t.file)), func(std::move(t.func)), line(std::move(t.line)) + { + // NOP + } + + /** + * デストラクタ。 + */ + AssertionError::~AssertionError() noexcept + { + // NOP + } + + /** + * エラーが発生したファイル名を返します。 + * + * @return ファイル名 + */ + const char *AssertionError::getFile() const noexcept + { + return file; + } + + /** + * エラーが発生した関数名を返します。 + * + * @return 関数名 + */ + const char *AssertionError::getFunc() const noexcept + { + return func; + } + + /** + * エラーが発生した行番号を返します。 + * + * @return 行番号 + */ + int AssertionError::getLine() const noexcept + { + return line; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 文字列表現 + */ + String AssertionError::toString() const noexcept + { + std::ostringstream ss; + ss << "AssertionError: " << message << std::endl + << "\tat " << file << ":" << line << " [" << func << "]"; + return String(ss.str()); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/cppunit.cpp b/modules/j/base/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/base/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/modules/j/base/src/dl.cpp b/modules/j/base/src/dl.cpp new file mode 100644 index 0000000..45ac601 --- /dev/null +++ b/modules/j/base/src/dl.cpp @@ -0,0 +1,126 @@ +#include + +#if (!IS_WINDOWS) +#include +#endif + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Dl を構築します。 + */ + Dl::Dl(const String &fileName) noexcept + { +#if (IS_WINDOWS) + handle = ::LoadLibraryEx(fileName); +#else + handle = ::dlopen(fileName, RTLD_LAZY); +#endif + // TODO : handle == 0 の場合エラー + } + + /** + * Dl のコピーコンストラクタ。 + * + * @param dl コピー元オブジェクト + */ + Dl::Dl(const Dl &dl) noexcept : Object(dl), handle(dl.handle) + { /* NOP */ + } + + /** + * Dl のムーブコンストラクタ。 + * + * @param dl ムーブ元オブジェクト + */ + Dl::Dl(Dl &&dl) noexcept : Object(std::move(dl)), handle(std::move(dl.handle)) + { /* NOP */ + } + + // デストラクタ + Dl::~Dl() noexcept + { +#if (IS_WINDOWS) + ::FreeLibrary(handle); +#else + ::dlclose(handle); +#endif + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param dl コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(const Dl &dl) noexcept + { + if (this != &dl) + { + Object::operator=(dl); + handle = dl.handle; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param dl ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Dl &Dl::operator=(Dl &&dl) noexcept + { + if (this != &dl) + { + Object::operator=(std::move(dl)); + } + return *this; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Dl::toString() const noexcept + { + return Object::toString(); + } + + /** + * 指定されたシンボルがロードされたメモリのアドレスを返します。 + * + * @code + * typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG framesToSkip, ULOLNG framesToCapture, PVOID *backTrace, PULONG backTraceHash); + * Dl dl("kernel32.dll"); + * RtlCaptureStackBackTraceDef RtlCaptureStackBackTrace = reinterpret_cast(dl.sym("RtlCaptureStackBackTrace")); + * void* buffer[64]; + * int cnt = RtlCaptureStackBackTrace(0, 64, buffer, 0); + * for (int i = 0; i < cnt; i++) + * { + * std::cout << buffer[i] << std::endl; + * } + * @endcode + * + * @param symbol シンボル名 + * @return シンボルのアドレス + */ + dl_func_t Dl::sym(const String &symbol) + { +#if (IS_WINDOWS) + return ::GetProcAddress(handle, symbol); +#else + return ::dlsym(handle, symbol); +#endif + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/env.cpp b/modules/j/base/src/env.cpp new file mode 100644 index 0000000..3044cfa --- /dev/null +++ b/modules/j/base/src/env.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Env + { + + /** + * 指定された環境変数の値を取得します。 + * name に = が含まれている場合、= の前までをキーとして + * 環境変数を検索します。 + * + * @param name 環境変数名 + * @return 値 + */ + String get(const String &name) + { + String keyName = name; + int eqPos = keyName.indexOf('='); + if (eqPos >= 0) + { + keyName = std::move(keyName.substring(0, eqPos)); + } +#if (IS_WINDOWS) + constexpr DWORD J_ENV_BUFFER_MAX = 32767; // Windows の環境変数の最大長は 327676 + unique_ptr buff = make_unique(J_ENV_BUFFER_MAX); + DWORD ret = GetEnvironmentVariable(keyName, &buff[0], J_ENV_BUFFER_MAX); + if (ret != 0) + { + return String(buff.get()); + } + return String(); +#else + return String(getenv(keyName)); +#endif + } + + /** + * 指定された name の環境変数が存在しない場合、環境変数 name に指定された値 value を設定します。 + * overwrite が true の場合、その値を value に変更します。 + * false の場合、name の値を変更せず true を返します。 + * name に '=' 文字が含まれている場合、設定に失敗し false を返します。 + * + * @param name 環境変数名 + * @param value 値 + * @param overwrite true/false (上書きする/しない) + * @return true/false (設定成功/失敗) + */ + bool set(const String &name, const String &value, bool overwrite) + { +#if (IS_WINDOWS) + int eqPos = name.indexOf('='); + if (eqPos >= 0) + { + return false; + } + char buff[1]; + DWORD ret = GetEnvironmentVariable(name, buff, 1); + if ((!overwrite) && (ret != 0)) + { // 既に環境変数が存在し、overwrite が false のため成功 + return true; + } + ret = SetEnvironmentVariable(name, value); + return (ret != 0); +#else + int overwriteFlag = overwrite ? 1 : 0; + int ret = setenv(name, value, overwriteFlag); + return (ret == 0); +#endif + } + + /** + * 変数 name の環境変数を削除します。 + * + * @param name 環境変数名 + * @return true/false (成功/失敗) + */ + bool unset(const String &name) + { +#if (IS_WINDOWS) + BOOL ret = SetEnvironmentVariable(name, NULL); + return (ret != 0); +#else + int ret = unsetenv(name); + return (ret == 0); +#endif + } + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/errno.cpp b/modules/j/base/src/errno.cpp new file mode 100644 index 0000000..510625e --- /dev/null +++ b/modules/j/base/src/errno.cpp @@ -0,0 +1,108 @@ +#include +#include + +#include + +namespace j +{ + namespace lang + { + namespace Errno + { +#if (IS_WINDOWS) + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows + // + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + SetLastError(errnum); + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return GetLastError(); + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + LPVOID lpMsgBuf; + int ret = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // 動作フラグ + 0, // メッセージ定義位置 + errnum, // エラーコード + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 言語ID + (LPSTR)&lpMsgBuf, // バッファアドレス + 0, // バッファサイズ + 0); // 挿入句 + + if (ret != 0) + { + String msg((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + return msg; + } + else + { + String msg(); + return msg; + } + } +#else + //////////////////////////////////////////////////////////////////////////////// + // + // For Windows 以外 + // + + /** + * エラー番号を設定します。 + * + * @param errnum エラー番号 + */ + void set(int errnum) + { + errno = errnum; + } + + /** + * エラー番号を取得します。 + * + * @return エラー番号 + */ + int get() + { + return errno; + } + + /** + * エラー番号に対応するエラーメッセージを取得します。 + * + * @param errnum エラー番号 + * @return エラーメッセージ + */ + String message(int errnum) + { + String msg(strerror(errnum)); + return msg; + } +#endif // IS_WINDOWS + + } // namespace Errno + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/error.cpp b/modules/j/base/src/error.cpp new file mode 100644 index 0000000..bf8dacb --- /dev/null +++ b/modules/j/base/src/error.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Error を構築します。 + */ + Error::Error() noexcept : Throwable() + { + // NOP + } + + /** + * Error を構築します。 + * + * @param msg メッセージ + */ + Error::Error(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Error のコピーコンストラクタ。 + * + * @param t コピー元 Error + */ + Error::Error(const Error &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Error のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Error::Error(Error &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Error::~Error() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/exception.cpp b/modules/j/base/src/exception.cpp new file mode 100644 index 0000000..98aafb5 --- /dev/null +++ b/modules/j/base/src/exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * Exception を構築します。 + */ + Exception::Exception() noexcept : Throwable() + { + // NOP + } + + /** + * Exception を構築します。 + * + * @param msg メッセージ + */ + Exception::Exception(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * Exception のコピーコンストラクタ。 + * + * @param t コピー元 Exception + */ + Exception::Exception(const Exception &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * Exception のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Exception::Exception(Exception &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + Exception::~Exception() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/illegal_argument_exception.cpp b/modules/j/base/src/illegal_argument_exception.cpp new file mode 100644 index 0000000..445f866 --- /dev/null +++ b/modules/j/base/src/illegal_argument_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IllegalArgumentException を構築します。 + */ + IllegalArgumentException::IllegalArgumentException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IllegalArgumentException を構築します。 + * + * @param msg メッセージ + */ + IllegalArgumentException::IllegalArgumentException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IllegalArgumentException のコピーコンストラクタ。 + * + * @param t コピー元 IllegalArgumentException + */ + IllegalArgumentException::IllegalArgumentException(const IllegalArgumentException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IllegalArgumentException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IllegalArgumentException::IllegalArgumentException(IllegalArgumentException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IllegalArgumentException::~IllegalArgumentException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/index_out_of_bounds_exception.cpp b/modules/j/base/src/index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..eabe527 --- /dev/null +++ b/modules/j/base/src/index_out_of_bounds_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * IndexOutOfBoundsException を構築します。 + */ + IndexOutOfBoundsException::IndexOutOfBoundsException() noexcept : RuntimeException() + { + // NOP + } + + /** + * IndexOutOfBoundsException を構築します。 + * + * @param msg メッセージ + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * IndexOutOfBoundsException のコピーコンストラクタ。 + * + * @param t コピー元 IndexOutOfBoundsException + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(const IndexOutOfBoundsException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * IndexOutOfBoundsException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + IndexOutOfBoundsException::IndexOutOfBoundsException(IndexOutOfBoundsException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + IndexOutOfBoundsException::~IndexOutOfBoundsException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/memory.cpp b/modules/j/base/src/memory.cpp new file mode 100644 index 0000000..d215406 --- /dev/null +++ b/modules/j/base/src/memory.cpp @@ -0,0 +1,756 @@ +#include + +#include +#include +#include +#include +#include +#include + +// 本来の malloc, free を利用するため j/memory.hpp を読み込む前に +// J_MEMORY_RAW を定義することで、malloc, free のマクロを定義させない +#define J_MEMORY_RAW +#include + +namespace j +{ + + //////////////////////////////////////////////////////////////////////// + // + // メモリ管理 エントリ操作 (内部用) + // + namespace + { + // ===================================================================== + // 定数、変数宣言 + // ===================================================================== + constexpr int PADDING = sizeof(void *) * 2; + bool isMemoryInitialized = false; //!< 初期化済みか否か + MemoryEntry memHead; //!< 管理メモリ先頭 + MemoryEntry memTail; //!< 管理メモリ末尾 + MemoryEntry memError; //!< エラー時の情報出力用 + std::recursive_mutex memMtx; //!< 排他処理用 + + // ===================================================================== + // プロトタイプ宣言 + // ===================================================================== + std::new_handler getNewHandler(); + void init(); + void add(MemoryEntry *entry); + void remove(MemoryEntry *entry); + MemoryEntry *newEntry( + MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deleteEntry(MemoryEntry *entry); + void *allocate( + size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void *reallocate( + void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line); + void deallocate( + void *ptr, + MemoryMark mark, const char *file, const char *func, int line); + const char *getMemoryMarkErrorMessage(MemoryMark mark); + + // ===================================================================== + // 内部関数 + // ===================================================================== + + /** + * new ハンドラを取得します。 + */ + std::new_handler getNewHandler() + { + std::new_handler p = std::set_new_handler(0); + std::set_new_handler(p); + return p; + } + + /** + * メモリ管理初期化 + */ + void init() + { + if (!isMemoryInitialized) + { + isMemoryInitialized = true; + memHead.size = memTail.size = 0; + memHead.mark = memTail.mark = MemoryMark::DELETED; + memHead.file = memTail.file = ""; + memHead.func = memTail.func = ""; + memHead.line = memTail.line = 0; + memHead._prev = memHead._next = &memTail; + memTail._prev = memTail._next = &memHead; + memHead._padding[0] = memTail._padding[0] = nullptr; + memHead._padding[1] = memTail._padding[1] = nullptr; + memHead._data = memTail._data = nullptr; + + // エラー処理用 + memError.size = 0; + memError.mark = MemoryMark::DELETED; + memError.file = ""; + memError.func = ""; + memError.line = 0; + memError._prev = memError._next = nullptr; + } + } + + /** + * 指定されたメモリエントリをメモリ管理に追加します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 追加するエントリ + */ + void add(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_next = &memTail; + entry->_prev = memTail._prev; + memTail._prev->_next = entry; + memTail._prev = entry; + MemoryManager::listener.alloc(*entry); + } + + /** + * 指定されたメモリエントリをメモリ管理より削除します。 + * リスナが登録されている場合、通知します。 + * + * @param entry 削除するエントリ + */ + void remove(MemoryEntry *entry) + { + init(); + std::lock_guard lock(memMtx); + entry->_prev->_next = entry->_next; + entry->_next->_prev = entry->_prev; + MemoryManager::listener.free(*entry); + } + + /** + * エラー発生時の通知をします。 + * リスナが登録されていない場合、何もしません。 + * + * @param msg エラーメッセージ + * @param size メモリサイズ + * @param mark メモリ状態 + * @param file ファイル + * @param func 関数 + * @param line 行番号 + */ + void notifyError(const char *msg, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + init(); + std::lock_guard lock(memMtx); + memError.size = size; + memError.mark = mark; + memError.file = file; + memError.func = func; + memError.line = line; + MemoryManager::listener.error(memError, msg); + } + + /** + * MemoryEntry を構築します。 + * + * @param entry エントリ + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + MemoryEntry *newEntry(MemoryEntry *entry, std::size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + MemoryEntry *newEntry = nullptr; + // メモリ確保 [@see C++ Programming Language 3rd $14.4.5] + for (;;) + { + // メモリ確保 + if ((entry == NULL) && (alignment > 0) && IS_MANAGED_ALIGNED(mark)) + { + newEntry = static_cast(MemoryManager::raw_aligned_alloc( + alignment, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + else + { + newEntry = static_cast(MemoryManager::raw_realloc( + entry, static_cast(sizeof(MemoryEntry) + size + PADDING))); + } + + if (newEntry != nullptr) + { // 確保成功のため抜ける + break; + } + + if (std::new_handler nhandler = getNewHandler()) + { + nhandler(); + } + else + { + return nullptr; + } + } + + newEntry->size = size; + newEntry->mark = mark; + newEntry->file = file; + newEntry->func = func; + newEntry->line = line; + newEntry->_data = (newEntry + 1); + return newEntry; + } + + /** + * MemoryEntry を破棄します。 + * + * @param entry エントリ + */ + void deleteEntry(MemoryEntry *entry) + { + entry->mark = MemoryMark::DELETED; + entry->size = 0; +#if (IS_WINDOWS) + if (IS_MANAGED_ALIGNED(entry->mark)) + { + ::_aligned_free(entry); + return; + } +#endif + MemoryManager::raw_free(entry); + } + + /** + * 指定されたサイズのメモリを確保します。 + * 確保に失敗した場合、nullptr を返します。 + * alignment > 0 の場合、アライメント指定でメモリを確保します。 + * + * @param alignment アライメント + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *allocate(size_t alignment, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + void *dataPtr = nullptr; + MemoryEntry *entry = newEntry(nullptr, alignment, size, mark, file, func, line); + if (entry) + { + add(entry); + dataPtr = entry->_data; + } + else + { // メモリ確保に失敗したためエラー通知する。 + notifyError("can't allocate", size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定された ptr のメモリサイズを変更します。 + * ptr = nullptr の場合は、allocate の alignment = 0 と同様の動作となります。 + * 確保に失敗した場合、nullptr を返します。 + * + * @param ptr ポインタ + * @param size 確保するメモリサイズ + * @param mark メモリ状態 + * @param file ファイル名 + * @param func 関数名 + * @param line 行番号 + */ + void *reallocate(void *ptr, std::size_t size, + MemoryMark mark, const char *file, const char *func, int line) + { + if (size == 0) + { // size が 0 の場合、free と等価 + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + return nullptr; + } + + if (ptr == nullptr) + { // ptr が nullptr の場合は、malloc と等価 + return allocate(0, size, mark, file, func, line); + } + + void *dataPtr = nullptr; + MemoryEntry *entry = static_cast(ptr); + entry--; + if (entry->mark == MemoryMark::ALLOCATED) + { + remove(entry); + MemoryEntry *nEntry = newEntry(entry, 0, size, MemoryMark::ALLOCATED, file, func, line); + if (nEntry) + { + add(nEntry); + dataPtr = nEntry->_data; + } + else + { // メモリ確保失敗 -> 元の領域は解放されていないためそのまま再管理する。 + add(entry); + notifyError("can't realloc", size, mark, file, func, line); + } + } + else + { // ALLOCATED 以外はエラー + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, size, mark, file, func, line); + } + return dataPtr; + } + + /** + * 指定されたポインタのメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param mark 期待されるメモリ状態 + * @param file ファイル + * @param func 関数名 + * @param line 行番号 + */ + void deallocate(void *ptr, + MemoryMark mark, const char *file, const char *func, int line) + { + if (ptr == nullptr) + { // nullptr に対しては何もしない。 + return; + } + + MemoryEntry *entry = static_cast(ptr); + entry--; + + if (IS_MANAGED_MEMORY(entry->mark)) + { // 管理メモリ + if ((entry->mark == mark) || (IS_MANAGED_ALIGNED(entry->mark) && (TO_NO_ALIGNED(entry->mark) == mark))) + { // 期待するメモリ状態と合致、または、実際のメモリ状態が期待する状態のアライメント状態 + remove(entry); + deleteEntry(entry); + } + else + { // エラー処理 + const char *errMsg = getMemoryMarkErrorMessage(entry->mark); + notifyError(errMsg, entry->size, mark, file, func, line); + } + } + else + { // 管理外メモリのため、通常の free で解放する。 + MemoryManager::raw_free(ptr); + } + } + + /** + * reallocate におけるエラーメッセージを取得します。 + * + * @param mark メモリ状態 + * @return エラーメッセージ + */ + const char *getMemoryMarkErrorMessage(MemoryMark mark) + { + switch (mark) + { + case ALLOCATED: + return "invalid pointer (memory allocated by allocated)"; + case ALLOCATED_ALIGNED: + return "unspported pointer (memory allocated by 'aligned_alloc')"; + case ALLOCATED_NEW: + return "invalid pointer (memory allocated by 'new')"; + case ALLOCATED_NEW_ARRAY: + return "invalid pointer (memory allocated by 'new[]')"; + case DELETED: + [[fallthrough]]; + default: + return "invalid pointer"; + } + } + + } // namespace + + //////////////////////////////////////////////////////////////////////////// + // + // メモリ管理 + // + + namespace MemoryManager + { + DefaultMemoryListener listener; //!< 通知用メモリリスナ + thread_local const char *gFile = ""; //!< 一時保存用 ファイル名 + thread_local const char *gFunc = ""; //!< 一時保存用 関数名 + thread_local int gLine = 0; //!< 一時保存用 行番号 + + /** + * 管理している全メモリエントリを引数に指定されたハンドラを実行します。 + * ハンドラが false を返す場合、処理が中断されます。 + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void entries(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + + { + std::lock_guard lock(memMtx); + bool isContinue = true; + for (MemoryEntry *entry = memHead._next; + isContinue && (entry != &memTail); + entry = entry->_next) + { + isContinue = handler(*entry, arg); + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたハンドラが、true を返す管理メモリをクリア(削除)します。 + * 本メソッド実行時、管理しているメモリの情報を引数に指定された関数が実行されます。 + * + * 使用例) + * @code + * bool canFree(const MemoryEntry&, void*) + * { // 管理しているメモリをすべてクリアする。 + * return true; + * } + * MemoryManager::freeif(&canFree); + * @endcode + * + * @param handler ハンドラ + * @param arg ハンドラに渡す情報 + */ + void freeif(bool (*handler)(const MemoryEntry &entry, void *arg), void *arg) + { + if (isMemoryInitialized) + { + + std::lock_guard lock(memMtx); + MemoryEntry *entry = memHead._next; + while (entry != &memTail) + { + MemoryEntry *nextEntry = entry->_next; + bool isFree = handler(*entry, arg); + + if (isFree) + { + remove(entry); + deleteEntry(entry); + } + entry = nextEntry; + } + } + // else 未初期化の場合は、エントリがないため何もしない。 + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *malloc(std::size_t size, const char *file, const char *func, int line) + { + return allocate(0, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *aligned_alloc(std::size_t alignment, std::size_t size, const char *file, const char *func, int line) + { + return allocate(alignment, size, MemoryMark::ALLOCATED_ALIGNED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *calloc(std::size_t nmemb, std::size_t size, const char *file, const char *func, int line) + { + if ((nmemb == 0) || (size == 0)) + { + return nullptr; + } + + if (nmemb > std::numeric_limits::max() / size) + { // オーバーフロー検知 + return nullptr; + } + + std::size_t n = nmemb * size; + void *ptr = allocate(0, n, MemoryMark::ALLOCATED, file, func, line); + if (ptr) + { // calloc のためゼロクリアする。 + std::memset(ptr, 0x00, n); + } + return ptr; + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @param file メモリ確保ファイル名 + * @param func メモリ確保関数名 + * @param line メモリ確保行番号 + * @return 確保したメモリへのポインタ + */ + void *realloc(void *ptr, std::size_t size, const char *file, const char *func, int line) + { + return reallocate(ptr, size, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + * @param file メモリ解放ファイル名 + * @param func メモリ解放関数名 + * @param line メモリ解放行番号 + */ + void free(void *ptr, const char *file, const char *func, int line) + { + deallocate(ptr, MemoryMark::ALLOCATED, file, func, line); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_malloc(std::size_t size) + { + return ::malloc(size); + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param alignement アライメント + * @param size 確保するメモリサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_aligned_alloc(std::size_t alignment, std::size_t size) + { +#if (IS_WINDOWS) + return ::_aligned_alloc(alignment, size); +#else + return ::aligned_alloc(alignment, size); +#endif + } + + /** + * 指定されたサイズのメモリを確保します。 + * + * @param nmemb 確保するメモリの要素数 + * @param size 要素のサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_calloc(std::size_t nmemb, std::size_t size) + { + return ::calloc(nmemb, size); + } + + /** + * ポインタが示すメモリブロックのサイズを変更します。 + * + * @param ptr ポインタ + * @param size 確保するメモリのサイズ + * @return 確保したメモリへのポインタ + */ + void *raw_realloc(void *ptr, std::size_t size) + { + return ::realloc(ptr, size); + } + + /** + * 指定されたメモリを解放します。 + * + * @param ptr 解放するメモリへのポインタ + */ + void raw_free(void *ptr) + { + return ::free(ptr); + } + + } // namespace MemoryManager + +} // namespace j + +//////////////////////////////////////////////////////////////////////// +// +// new/delete オーバーライド +// + +void *operator new(std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size) +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al) +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + if (p == nullptr) + { + throw std::bad_alloc(); + } + return p; +} + +void *operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(0, size, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &) noexcept +{ + void *p = j::allocate(static_cast(al), size, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); + return p; +} + +void operator delete(void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete(void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete(void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} + +void operator delete[](void *p, std::size_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} +void operator delete[](void *p, std::size_t, std::align_val_t) noexcept +{ + j::deallocate(p, j::MemoryMark::ALLOCATED_NEW_ARRAY_ALIGNED, + j::MemoryManager::gFile, + j::MemoryManager::gFunc, + j::MemoryManager::gLine); +} diff --git a/modules/j/base/src/memory_entry.cpp b/modules/j/base/src/memory_entry.cpp new file mode 100644 index 0000000..53f6804 --- /dev/null +++ b/modules/j/base/src/memory_entry.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include + +#include + +#define PRINT_ASCII(c) (((0x20 <= c) && (c < 0x7F)) ? c : '.') + +namespace j +{ + namespace + { + constexpr int MAX_BUFFER_SIZE = 1024; + } + + /** + * 指定された出力ストリームにメモリエントリの情報を出力します。 + * + * @param os 出力ストリーム + * @param entry メモリエントリ + * @param bytes 出力するバイト最大バイト数 + * @param binary true の場合、データの16進数情報が出力されます。 + * @param ascii true の場合、データのASCII 情報が出力されます。 + * @param maxColumn 出力文字数 + * @param noEndl 改行出力無し + */ + void MemoryEntry::dump( + std::ostream &os, int bytes, bool binary, bool ascii, int maxColumn, bool noEndl) const + { + // <ファイル名>:<行番号> ( bytes) [<関数名>] 部分のカラム数を算出する。 + int infoColumn = maxColumn; + infoColumn -= (binary) ? (bytes * 3) + 3 : 0; + infoColumn -= (ascii) ? (bytes) + 3 : 0; + + if (infoColumn <= 0) + { // カラム数不足のため、基本データのみを出力 + printBase(os, maxColumn); + } + else + { // 基本データ出力 + printBase(os, infoColumn); + + if (binary) + { // 16進数ダンプ + os << " | "; + printBinary(os, bytes); + } + + if (ascii) + { // ASCII出力 + os << " | "; + printAscii(os, bytes); + } + } + // 改行出力 + if (!noEndl) + { + os << std::endl; + } + } + + /** + * <ファイル名>:<行番号> ( bytes) [<関数名>] を指定されたストリームに出力します。 + * column に従い、出力の切り詰めもしくは、スペース' 'によるパディングを行います。 + * + * @param os 出力ストリーム + * @param column カラム数 + */ + void MemoryEntry::printBase(std::ostream &os, int column) const + { + char buff[MAX_BUFFER_SIZE]; + int maxColumn = (column < MAX_BUFFER_SIZE) ? column : MAX_BUFFER_SIZE - 1; + int baseLength = snprintf(buff, MAX_BUFFER_SIZE, "%s:%d (%s) [%s]", file, line, getSizeStr(), func); + + os << buff; + if (baseLength < maxColumn) + { // パディング出力 + int padding = maxColumn - baseLength; + memset(buff, ' ', padding); + buff[padding] = '\0'; + os << buff; + } + } + + /** + * 指定された出力ストリームにバイナリの16進数ダンプを出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printBinary(std::ostream &os, int bytes) const + { + os << std::hex << std::setfill('0'); + int dataLen = (this->size < bytes) ? this->size : bytes; + unsigned char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << std::setw(2) << (dataPtr[idx] & 0xFF) << " "; + } + for (; idx < bytes; idx++) + { + os << "-- "; + } + } + + /** + * 指定された出力ストリームに ASCII を出力します。 + * + * @param os 出力ストリーム + * @param bytes 出力するバイト数 + */ + void MemoryEntry::printAscii(std::ostream &os, int bytes) const + { + int dataLen = (this->size < bytes) ? this->size : bytes; + char *dataPtr = static_cast(this->_data); + int idx = 0; + for (; idx < dataLen; idx++) + { + os << PRINT_ASCII(dataPtr[idx]); + } + for (; idx < bytes; idx++) + { + os << " "; + } + } + + /** + * サイズ情報取得 + */ + const char *MemoryEntry::getSizeStr() const + { + static char sizeStr[16]; + static constexpr const char *SIZE_UNIT[] = {" B", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + double viewSize = (double)this->size; + while (viewSize >= 1024) + { + viewSize /= 1024; + unitIndex++; + } + snprintf(sizeStr, sizeof(sizeStr), "%8.3lf %s", viewSize, SIZE_UNIT[unitIndex]); + return sizeStr; + } + +} // namespace j diff --git a/modules/j/base/src/memory_listener.cpp b/modules/j/base/src/memory_listener.cpp new file mode 100644 index 0000000..1920637 --- /dev/null +++ b/modules/j/base/src/memory_listener.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace j +{ + // [ALLOC] 等の文字数 + constexpr int HEAD_DATA_LENGTH = 8; + + /** + * デフォルトのメモリリスナを構築します。 + */ + DefaultMemoryListener::DefaultMemoryListener() noexcept + : isPrint(false), isPrintError(true), dumpBytes(16), maxColumn(130) + { + // NOP + } + + /** + * デストラクタ。 + */ + DefaultMemoryListener::~DefaultMemoryListener() noexcept + { + // NOP + } + + void DefaultMemoryListener::config(bool isPrint, bool isPrintError, int dumpBytes, int maxColumn) noexcept + { + this->isPrint = isPrint; + this->isPrintError = isPrintError; + this->dumpBytes = dumpBytes; + this->maxColumn = maxColumn; + } + + /** + * メモリ確保時に呼び出されます。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::alloc(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[ALLOC] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * メモリ解放時に表示します。 + * + * @param entry メモリ情報 + */ + void DefaultMemoryListener::free(const MemoryEntry &entry) noexcept + { + if (isPrint) + { + std::cerr << "[FREE ] "; + if (dumpBytes > 0) + { + entry.dump(std::cerr, dumpBytes, true, true, maxColumn - HEAD_DATA_LENGTH, true); + } + std::cerr << std::endl; + } + } + + /** + * エラー発生時に呼び出され、エラー情報を表示します。 + * + * @param entry エントリ(エラー発生した際の情報, _data 部分はアクセス不可) + * @param msg メッセージ + */ + void DefaultMemoryListener::error(const MemoryEntry &entry, const char *msg) noexcept + { + if (isPrintError) + { + std::cerr << "[ERROR] "; + int len = std::strlen(msg) + 1; + entry.dump(std::cerr, 0, false, false, maxColumn - HEAD_DATA_LENGTH - len, true); + std::cerr << " " << msg << std::endl; + } + } + +} // namespace j diff --git a/modules/j/base/src/object.cpp b/modules/j/base/src/object.cpp new file mode 100644 index 0000000..7fd248d --- /dev/null +++ b/modules/j/base/src/object.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Object を構築します。 + */ + Object::Object() noexcept { /* NOP */ } + + /** + * Object のコピーコンストラクタ。 + * + * @param obj コピー元オブジェクト + */ + Object::Object(const Object &) noexcept { /* NOP */ } + + /** + * Object のムーブコンストラクタ。 + * + * @param obj ムーブ元オブジェクト + */ + Object::Object(Object &&) noexcept { /* NOP */ } + + // デストラクタ + // virtual Object::~Object() noexcept = default; + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param obj コピー元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(const Object &) noexcept + { // 特にコピーする要素はない。 + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Object &Object::operator=(Object &&) noexcept + { // 特に移すものはない。 + return *this; + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator==(const Object &obj) const noexcept + { + return equals(obj); + } + + /** + * 比較演算子。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致/不一致) + */ + bool Object::operator!=(const Object &obj) const noexcept + { + return !equals(obj); + } + + /** + * クラス名を取得します。 + * + * @return クラス名 + */ + String Object::getClassName() const noexcept + { + String str(typeid(*this).name()); + return str; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Object::toString() const noexcept + { + // String str(getClassName() + "@" + std::to_string(reinterpret_cast(this))); + return getClassName(); + } + + /** + * 指定されたオブジェクトが同じクラスか否かを返します。 + * + * @param obj オブジェクト + * @return true/false (同じクラス/異なるクラス) + */ + bool Object::isSameClass(const Object &obj) const noexcept + { + return (typeid(*this) == typeid(obj)); + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool Object::equals(const Object &obj) const noexcept + { + if (isSameClass(obj)) + { // 同じクラス + int ownHash = hashCode(); + int objHash = obj.hashCode(); + if (ownHash == objHash) + { // ハッシュコードが一緒 + String ownStr = toString(); + String objStr = obj.toString(); + return ownStr.equals(objStr); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int Object::hashCode() const noexcept + { + return (reinterpret_cast(this)); + } + + /** + * 待機中のスレッドを再開します。 + */ + void Object::notify() + { + std::lock_guard lock(mtx); + cv.notify_one(); + } + + /** + * 待機中のすべてのスレッドを再開します。 + */ + void Object::notifyAll() + { + std::lock_guard lock(mtx); + cv.notify_all(); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait() + { + std::unique_lock lock(mtx); + cv.wait(lock); + } + + /** + * 現在のスレッドを待機させます。 + */ + void Object::wait(int msec) + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::milliseconds(msec)); + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Object::clone() const noexcept + { + return std::make_unique(*this); + } + + /** + * 出力用 + * + * @param os output stream + * @param obj オブジェクト + */ + std::ostream &operator<<(std::ostream &os, const Object &obj) + { + os << obj.toString(); + return os; + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/runtime_exception.cpp b/modules/j/base/src/runtime_exception.cpp new file mode 100644 index 0000000..16c609f --- /dev/null +++ b/modules/j/base/src/runtime_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * RuntimeException を構築します。 + */ + RuntimeException::RuntimeException() noexcept : Throwable() + { + // NOP + } + + /** + * RuntimeException を構築します。 + * + * @param msg メッセージ + */ + RuntimeException::RuntimeException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * RuntimeException のコピーコンストラクタ。 + * + * @param t コピー元 RuntimeException + */ + RuntimeException::RuntimeException(const RuntimeException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * RuntimeException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + RuntimeException::RuntimeException(RuntimeException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + RuntimeException::~RuntimeException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp new file mode 100644 index 0000000..cf3d8de --- /dev/null +++ b/modules/j/base/src/string.cpp @@ -0,0 +1,567 @@ +#include +#include + +#include + +// 入力ストリーム用バッファサイズ +static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; + +namespace j +{ + namespace lang + { + // [補足] + // std::unique_ptr value; において、 + // インデックスに対する操作も多々あるため、value.get() + index ではなく、 + // 直観的にわかりやすい, &value[index] の表現にて実装している。 + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const char *str) noexcept + { + setValue(str); + } + + /** + * String を構築します。 + * + * @param str 文字列 + */ + String::String(const std::string &str) noexcept + { + setValue(str.c_str()); + } + + /** + * String のコピーコンストラクタ。 + * + * @param str コピー元 String + */ + String::String(const String &str) noexcept : Object(str) + { + setValue(&str.value[0]); + } + + /** + * String のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + String::String(String &&str) noexcept : Object(std::move(str)), value(std::move(str.value)), len(str.len) + { + str.value = nullptr; + str.len = 0; + } + + /** + * デストラクタ。 + */ + String::~String() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + String &String::operator=(const String &str) noexcept + { + if (this != &str) + { + Object::operator=(str); + setValue(&str.value[0]); + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + String &String::operator=(String &&str) noexcept + { // 特に移すものはない。 + if (this != &str) + { + Object::operator=(std::move(str)); + value = std::move(str.value); + len = str.len; + str.value = nullptr; + str.len = 0; + } + return *this; + } + + /** + * 指定された文字列を結合します。 + */ + String &String::operator+=(const String &str) noexcept + { + int newLen = len + str.len; + std::unique_ptr newStr = std::make_unique(newLen + 1); + std::strncpy(&newStr[0], &value[0], len); + std::strncpy(&newStr[len], &str.value[0], str.len); + newStr[newLen] = '\0'; + value = std::move(newStr); + len = newLen; + return *this; + } + + /** + * const char* 型に変換します。 + */ + String::operator const char *() const + { + return value.get(); + } + + /** + * 文字列の長さを返します。 + * + * @return 文字列の長さ + */ + int String::length() const noexcept + { + return len; + } + + /** + * 指定された位置の文字を返します。 + * + * @param index 位置 + * @return 文字 + */ + char String::charAt(int index) const + { + if ((index < 0) || (index >= len)) + { + // TODO: IndexOutOfBoundsException + } + return value[index]; + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 + * @param endIndex 終了位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex, int endIndex) const + { + if ((0 <= beginIndex) && (beginIndex <= endIndex) && (endIndex <= len)) + { + int subLen = endIndex - beginIndex; + std::unique_ptr subStr = std::make_unique(subLen + 1); + std::strncpy(&subStr[0], &value[beginIndex], subLen); + subStr[subLen] = '\0'; + String result(&subStr[0]); + return result; + } + else + { + // TODO: IndexOutOfBoundsException + return nullptr; + } + } + + /** + * 指定された文字列が含まれるか否かを返します。 + * + * @param str 文字列 + * @return true/false (含まれる/含まれない) + */ + bool String::contains(const String &str) const noexcept + { + return (std::strstr(&value[0], &str.value[0]) != nullptr); + } + + /** + * 指定された文字を置換します。 + * + * @param oldChar 置換前文字 + * @param newChar 置換後文字 + * @return 置換された文字列 + */ + String String::replace(char oldChar, char newChar) const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + if (str.value[idx] == oldChar) + { + str.value[idx] = newChar; + } + } + return str; + } + + // 文字列置換 + String String::replace(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + String str(res.c_str()); + return str; + } + + // 文字列置換 + String String::replaceAll(const String ®ex, const String &replacement) const + { + std::regex re(®ex.value[0]); + std::string res = std::regex_replace( + &value[0], re, &replacement.value[0], std::regex_constants::match_any); + String str(res.c_str()); + return str; + } + + // 分割 + std::unique_ptr String::split(const String &) const noexcept + { + return nullptr; + } + + // 先頭の文字列が一致するか + bool String::startsWith(const String &prefix) const noexcept + { + if (prefix.len > len) + { + return false; + } + for (int idx = 0; idx < prefix.len; idx++) + { + if (value[idx] != prefix.value[idx]) + { + return false; + } + } + return true; + } + + // 末尾の文字列が一致するか + bool String::endsWith(const String &suffix) const noexcept + { + if (suffix.len > len) + { + return false; + } + int value_idx = (len - suffix.len); + for (int idx = 0; idx < suffix.len; idx++) + { + if (value[value_idx] != suffix.value[idx]) + { + return false; + } + value_idx++; + } + return true; + } + + // 小文字変換 + String String::toLowerCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::tolower(str.value[idx]); + } + return str; + } + + // 大文字変換 + String String::toUpperCase() const noexcept + { + String str(*this); + for (int idx = 0; idx < len; idx++) + { + str.value[idx] = std::toupper(str.value[idx]); + } + return str; + } + + // trim + String String::trim() const noexcept + { + int beginIndex = 0; + for (; beginIndex < len; beginIndex++) + { + if (value[beginIndex] > 0x20) + { + break; + } + } + int endIndex = len; + for (; endIndex >= beginIndex; endIndex--) + { + if (value[endIndex] > 0x20) + { + break; + } + } + int trimedLen = endIndex - beginIndex; + std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); + std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); + trimedStr[trimedLen] = '\0'; + String result(&trimedStr[0]); + return result; + } + + /** + * この文字列内で、指定された文字列が最初に出現する位置のインデックスを返します。 + * 存在しない場合、-1 を返します。 + * + * @param 検索対象の部分文字列 + * @param fromIndex 検索開始位置のインデックス + * @return 見つかった位置 + */ + int String::indexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx < this->len; idx++) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::indexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + const char *ptr = std::strstr(&value[fromIndex], &str.value[0]); + if (ptr != nullptr) + { + return (ptr - &value[0]); + } + return -1; + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch) const noexcept + { + return lastIndexOf(ch, this->len); + } + + /** + * この文字列内で、指定された文字が最後に出現する位置のインデックスを返します。 + * 見つからない場合 -1 を返します。 + * + * @param ch 検索する文字 + * @param fromIndex 検索開始位置 + * @return 見つかった位置 + */ + int String::lastIndexOf(int ch, int fromIndex) const noexcept + { + for (int idx = fromIndex; idx >= 0; idx--) + { + if (this->value[idx] == ch) + { + return idx; + } + } + return -1; + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、length() とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @return 見つかった位置 + */ + int String::lastIndexOf(const String &str) const noexcept + { + return lastIndexOf(str, this->len); + } + + /** + * この文字列内で、指定された部分文字列が最後に出現する位置のインデックスを返します。 + * 空の文字列「」が最後に出現する位置は、fromIndex とみなされます。 + * 見つからない場合 -1 を返します。 + * + * @param str 検索する文字列 + * @param fromIndex 検索開始位置 + * @return 見つかったインデックス + */ + int String::lastIndexOf(const String &str, int fromIndex) const noexcept + { + if (str.len == 0) + { // 空文字 + return fromIndex; + } + if (str.len > fromIndex) + { // 部分文字列の方が長いためマッチしない (-1 を返す) + return -1; + } + for (int idx = fromIndex - str.len; idx >= 0; idx--) + { + if (std::strncmp(&this->value[idx], &str.value[0], str.len) == 0) + { + return idx; + } + } + return -1; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String String::toString() const noexcept + { + String str(*this); + return str; + } + + /** + * 指定されたオブジェクトと合致するか否かを返します。 + * + * @param obj 比較するオブジェクト + * @return true/false (合致する/しない) + */ + bool String::equals(const Object &obj) const noexcept + { + bool isSame = isSameClass(obj); + if (isSame) + { + const String &str = dynamic_cast(obj); + if (len == str.len) + { + return (std::strcmp(&value[0], &str.value[0]) == 0); + } + } + return false; + } + + /** + * ハッシュコードを取得します。 + * + * @return ハッシュコード + */ + int String::hashCode() const noexcept + { + int hash = 0; + for (int idx = 0; idx < len; idx++) + { + hash = 31 * hash + value[idx]; + } + return hash; + } + + /** + * 2つの文字列を結合します。 + * + * @param str1 文字列 + * @param str2 文字列 + * @return 結合後の文字列 + */ + String operator+(const String &str1, const String &str2) noexcept + { + String str = str1; + str += str2; + return str; + } + + /** + * 出力用 + * + * @param os output stream + * @param str 出力文字列 + * @return output stream + */ + std::ostream &operator<<(std::ostream &os, const String &str) + { + if (str.value != nullptr) + { + os << &str.value[0]; + } + return os; + } + + /** + * 入力用 + * + * @param is input stream + * @param str 入力先 String + * @return input stream + */ + std::istream &operator>>(std::istream &is, String &str) + { + char buff[MAX_ISTREAM_BUFFER_SIZE]; + is >> buff; + str = String(buff); + return is; + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 文字列データを設定します。 + * + * @param str 設定する文字列 + */ + void String::setValue(const char *str) + { + if (str) + { + len = std::strlen(str); + value = std::make_unique(len + 1); + std::strcpy(&value[0], str); + } + else + { + len = 0; + value = std::make_unique(1); + value[0] = '\0'; + } + } + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr String::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/term.cpp b/modules/j/base/src/term.cpp new file mode 100644 index 0000000..33a6c71 --- /dev/null +++ b/modules/j/base/src/term.cpp @@ -0,0 +1,34 @@ +#include + +namespace j +{ + namespace io + { + namespace Term + { + namespace Cursor + { + /** + * 指定されたコード、値をもとにエスケープシーケンスを作成します。 + * + * @param code コード + * @param n 移動量など + * @param delim 区切り文字 + * @param m 移動量など + * @return エスケープシーケンス文字列 + */ + std::string_view MAKE_ESC_SEQ(const char *code, int n, const char *delim, int m) + { + thread_local static std::string result; + result = "\x1b[" + std::to_string(n); + if (delim) + { + result += delim + std::to_string(m); + } + result += code; + return {result}; + } + } + } + } +} diff --git a/modules/j/base/src/throwable.cpp b/modules/j/base/src/throwable.cpp new file mode 100644 index 0000000..e42b5d6 --- /dev/null +++ b/modules/j/base/src/throwable.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include +#include + +namespace j +{ + namespace lang + { + + /** + * Throwable を構築します。 + */ + Throwable::Throwable() noexcept : message(Errno::message(Errno::get())) + { + // NOP + } + + /** + * Throwable を構築します。 + * + * @param msg メッセージ + */ + Throwable::Throwable(const String &msg) noexcept : message(msg) + { + // NOP + } + + /** + * Throwable のコピーコンストラクタ。 + * + * @param t コピー元 Throwable + */ + Throwable::Throwable(const Throwable &t) noexcept : Object(t), message(t.message) + { + // NOP + } + + /** + * Throwable のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + Throwable::Throwable(Throwable &&t) noexcept : Object(std::move(t)), message(std::move(t.message)) + { + t.message = nullptr; + } + + /** + * デストラクタ。 + */ + Throwable::~Throwable() noexcept + { + // NOP + } + + /** + * コピー代入演算子。 + * コピーして代入します。 + * + * @param str コピー元 String + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(const Throwable &t) noexcept + { + if (this != &t) + { + Object::operator=(t); + message = t.message; + } + return *this; + } + + /** + * ムーブ代入演算子。 + * + * @param obj ムーブ元オブジェクト + * @return 本オブジェクトへの参照 + */ + Throwable &Throwable::operator=(Throwable &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + message = std::move(t.message); + t.message = nullptr; + } + return *this; + } + + /** + * エラーメッセージを取得します。 + * + * @return エラーメッセージ + */ + String Throwable::getMessage() const noexcept + { + return message; + } + + /** + * 本オブジェクトの文字列表現を返します。 + * + * @return 本オブジェクトの文字列表現 + */ + String Throwable::toString() const noexcept + { + return getMessage(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // protected + // + + /** + * 本オブジェクトの複製を取得します。 + * + * [備考] + * 派生クラスが、ポリモーフィズムをサポートするために、 + * unique_ptr を返すようにしています。 + * + * @return 本オブジェクトの複製 + */ + std::unique_ptr Throwable::clone() const noexcept + { + return std::make_unique(*this); + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/unsupported_operation_exception.cpp b/modules/j/base/src/unsupported_operation_exception.cpp new file mode 100644 index 0000000..4a9dd54 --- /dev/null +++ b/modules/j/base/src/unsupported_operation_exception.cpp @@ -0,0 +1,54 @@ +#include + +namespace j +{ + namespace lang + { + /** + * UnsupportedOperationException を構築します。 + */ + UnsupportedOperationException::UnsupportedOperationException() noexcept : RuntimeException() + { + // NOP + } + + /** + * UnsupportedOperationException を構築します。 + * + * @param msg メッセージ + */ + UnsupportedOperationException::UnsupportedOperationException(const String &msg) noexcept : RuntimeException(msg) + { + // NOP + } + + /** + * UnsupportedOperationException のコピーコンストラクタ。 + * + * @param t コピー元 UnsupportedOperationException + */ + UnsupportedOperationException::UnsupportedOperationException(const UnsupportedOperationException &t) noexcept : RuntimeException(t) + { + // NOP + } + + /** + * UnsupportedOperationException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + UnsupportedOperationException::UnsupportedOperationException(UnsupportedOperationException &&t) noexcept : RuntimeException(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + UnsupportedOperationException::~UnsupportedOperationException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/Makefile b/modules/j/base/test/Makefile new file mode 100644 index 0000000..b8d8a1c --- /dev/null +++ b/modules/j/base/test/Makefile @@ -0,0 +1,54 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = ut +TARGET = $(NAME).exe +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include -I../src +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := test + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp new file mode 100644 index 0000000..21015f2 --- /dev/null +++ b/modules/j/base/test/src/ut.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "ut_assertion_error.cpp" +#include "ut_dl.cpp" +#include "ut_env.cpp" +#include "ut_errno.cpp" +#include "ut_exception.cpp" +#include "ut_illegal_argument_exception.cpp" +#include "ut_index_out_of_bounds_exception.cpp" +#include "ut_runtime_exception.cpp" +#include "ut_term.cpp" +#include "ut_throwable.cpp" +#include "ut_unsupported_operation_exception.cpp" + +using namespace j; +using namespace j::cppunit; + +int main() +{ + { AssertionErrorTest tc; tc.suite(); } + { DlTest tc; tc.suite(); } + { EnvTest tc; tc.suite(); } + { ErrnoTest tc; tc.suite(); } + { ExceptionTest tc; tc.suite(); } + { IllegalArgumentExceptionTest tc; tc.suite(); } + { IndexOutOfBoundsExceptionTest tc; tc.suite(); } + { RuntimeExceptionTest tc; tc.suite(); } + { TermTest tc; tc.suite(); } + { ThrowableTest tc; tc.suite(); } + { UnsupportedOperationExceptionTest tc; tc.suite(); } + testManager.printResult(); + return 0; +} diff --git a/modules/j/base/test/src/ut_assertion_error.cpp b/modules/j/base/test/src/ut_assertion_error.cpp new file mode 100644 index 0000000..2ae6a36 --- /dev/null +++ b/modules/j/base/test/src/ut_assertion_error.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class AssertionErrorTest : public TestCase +{ +public: + AssertionErrorTest() {} + ~AssertionErrorTest() {} + void setUp() {} + void tearDown() {} + + void testAssertionError() + { + AssertionError e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsg() + { + String msg = "ERROR MESSAGE"; + AssertionError e(msg); + assertEquals(msg, e.getMessage()); + assertEquals("", e.getFile()); + assertEquals("", e.getFunc()); + assertEquals(0, e.getLine()); + } + + void testAssertionErrorMsgFileFuncLine() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + } + + void testAssertionErrorCopy() + { + String msg = "ERROR MESSAGE 2"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = e; + assertEquals(msg, e.getMessage()); + assertEquals(file, e.getFile()); + assertEquals(func, e.getFunc()); + assertEquals(line, e.getLine()); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testAssertionErrorMove() + { + String msg = "ERROR MESSAGE 3"; + const char *file = __FILE__; + const char *func = __func__; + int line = __LINE__; + + AssertionError e(msg, file, func, line); + AssertionError ee = std::move(e); + + assertEquals(msg, ee.getMessage()); + assertEquals(file, ee.getFile()); + assertEquals(func, ee.getFunc()); + assertEquals(line, ee.getLine()); + } + + void testAssertionErrorToString() + { + String msg = "ERROR MESSAGE 3"; + const char *file = "SampleFile"; + const char *func = "SampleFunction"; + int line = 123; + + const char *exp = "AssertionError: ERROR MESSAGE 3\n\tat SampleFile:123 [SampleFunction]"; + + AssertionError e(msg, file, func, line); + assertEquals(exp, e.toString()); + } + + void suite() + { + RUN_TEST("new AssertionError()", testAssertionError); + RUN_TEST("new AssertionError(msg)", testAssertionErrorMsg); + RUN_TEST("new AssertionError(msg, ...)", testAssertionErrorMsgFileFuncLine); + RUN_TEST("copy", testAssertionErrorCopy); + RUN_TEST("move", testAssertionErrorMove); + RUN_TEST("toString()", testAssertionErrorToString); + } +}; diff --git a/modules/j/base/test/src/ut_dl.cpp b/modules/j/base/test/src/ut_dl.cpp new file mode 100644 index 0000000..9c0cceb --- /dev/null +++ b/modules/j/base/test/src/ut_dl.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +#if (IS_WINDOWS) +constexpr const char *DL_FILE = "./test-lib/libcalc.dll"; +#else +constexpr const char *DL_FILE = "./test-lib/libcalc.so"; +#endif + +class DlTest : public TestCase +{ +public: + DlTest() {} + ~DlTest() {} + void setUp() {} + void tearDown() {} + + void testDl() + { + Dl dl(DL_FILE); + typedef int (*CalcFunc)(int, int); + CalcFunc add = reinterpret_cast(dl.sym("calc_add")); + int ret = add(1, 2); + assertEquals(3, ret); + } + + void testCopy() + { + Dl dl(DL_FILE); + Dl dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testMove() + { + Dl dl(DL_FILE); + Dl dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc sub = reinterpret_cast(dl2.sym("calc_sub")); + int ret = sub(3, 2); + assertEquals(1, ret); + } + + void testOpEq() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + dl = dl; + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl.sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testOpEqMove() + { + Dl dl(DL_FILE); + Dl *dl2 = new Dl(DL_FILE); + *dl2 = std::move(dl); + typedef int (*CalcFunc)(int, int); + CalcFunc mul = reinterpret_cast(dl2->sym("calc_mul")); + int ret = mul(3, 2); + assertEquals(6, ret); + + *dl2 = std::move(*dl2); + typedef int (*CalcFunc)(int, int); + CalcFunc div = reinterpret_cast(dl2->sym("calc_div")); + ret = div(6, 2); + assertEquals(3, ret); + delete dl2; + } + + void testToString() + { + Dl dl(DL_FILE); + String str = dl.toString(); + + // 文字列中に, Dl が含まれるハズ。 (N1j4lang2DlE) + int idx = str.indexOf("Dl"); + assertTrue(idx >= 0); + } + + void suite() + { + RUN_TEST("new Dl()", testDl); + RUN_TEST("copy", testCopy); + RUN_TEST("move", testMove); + RUN_TEST("operator=(Dl &)", testOpEq); + RUN_TEST("operator=(Dl &&)", testOpEqMove); + RUN_TEST("toString()", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_env.cpp b/modules/j/base/test/src/ut_env.cpp new file mode 100644 index 0000000..443d960 --- /dev/null +++ b/modules/j/base/test/src/ut_env.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class EnvTest : public TestCase +{ +public: + EnvTest() {} + ~EnvTest() {} + void setUp() {} + void tearDown() + { + Env::unset("TEST_ENV"); + } + + void testGet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + // = 以降は、キーとして無視される。 + value = Env::get("TEST_ENV=TEST"); + assertEquals("SAMPLE", value); + } + + void testSet() + { + Env::set("TEST_ENV", "SAMPLE"); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + bool ret = Env::set("TEST_ENV", "SAMPLE2", true); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + ret = Env::set("TEST_ENV", "SAMPLE3", false); + assertTrue(ret); + value = Env::get("TEST_ENV"); + assertEquals("SAMPLE2", value); + + // = がキーに含まれるとエラー + ret = Env::set("TEST_ENV2=TEST", "ERRVAL"); + assertFalse(ret); + value = Env::get("TEST_ENV2"); + assertEquals(String(), value); + } + + void testUnset() + { + Env::set("TEST_ENV", "SAMPLE", true); + String value = Env::get("TEST_ENV"); + assertEquals("SAMPLE", value); + + Env::unset("TEST_ENV"); + + value = Env::get("TEST_ENV"); + assertEquals(String(), value); + } + + void suite() + { + RUN_TEST("Env::get", testGet); + RUN_TEST("Env::set", testSet); + RUN_TEST("Env::unset", testUnset); + } +}; diff --git a/modules/j/base/test/src/ut_errno.cpp b/modules/j/base/test/src/ut_errno.cpp new file mode 100644 index 0000000..9663f4b --- /dev/null +++ b/modules/j/base/test/src/ut_errno.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ErrnoTest : public TestCase +{ +public: + ErrnoTest() {} + ~ErrnoTest() {} + void setUp() {} + void tearDown() {} + + void testGet() + { + Errno::set(0); + int num = Errno::get(); + assertEquals(0, num); + } + + void testSet() + { + Errno::set(EINVAL); + int num = Errno::get(); + assertEquals(EINVAL, num); + } + + void testMessage() + { + String msg = Errno::message(EINVAL); + int len = msg.length(); + assertTrue(len > 0); + } + + void suite() + { + RUN_TEST("Errno::get", testGet); + RUN_TEST("Errno::set", testSet); + RUN_TEST("Errno::message", testMessage); + } +}; diff --git a/modules/j/base/test/src/ut_exception.cpp b/modules/j/base/test/src/ut_exception.cpp new file mode 100644 index 0000000..525c2da --- /dev/null +++ b/modules/j/base/test/src/ut_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ExceptionTest : public TestCase +{ +public: + ExceptionTest() {} + ~ExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testException() + { + Exception e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testExceptionMsg() + { + String msg = "ERROR MESSAGE"; + Exception e(msg); + assertEquals(msg, e.getMessage()); + } + + void testExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + Exception e(msg); + Exception ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + Exception e(msg); + Exception ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + Exception e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new Exception()", testException); + RUN_TEST("new Exception(msg)", testExceptionMsg); + RUN_TEST("copy", testExceptionCopy); + RUN_TEST("move", testExceptionMove); + RUN_TEST("toString()", testExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_illegal_argument_exception.cpp b/modules/j/base/test/src/ut_illegal_argument_exception.cpp new file mode 100644 index 0000000..f6e08dd --- /dev/null +++ b/modules/j/base/test/src/ut_illegal_argument_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IllegalArgumentExceptionTest : public TestCase +{ +public: + IllegalArgumentExceptionTest() {} + ~IllegalArgumentExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIllegalArgumentException() + { + IllegalArgumentException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIllegalArgumentExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IllegalArgumentException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIllegalArgumentExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIllegalArgumentExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IllegalArgumentException e(msg); + IllegalArgumentException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIllegalArgumentExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IllegalArgumentException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IllegalArgumentException()", testIllegalArgumentException); + RUN_TEST("new IllegalArgumentException(msg)", testIllegalArgumentExceptionMsg); + RUN_TEST("copy", testIllegalArgumentExceptionCopy); + RUN_TEST("move", testIllegalArgumentExceptionMove); + RUN_TEST("toString()", testIllegalArgumentExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp new file mode 100644 index 0000000..79a7041 --- /dev/null +++ b/modules/j/base/test/src/ut_index_out_of_bounds_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class IndexOutOfBoundsExceptionTest : public TestCase +{ +public: + IndexOutOfBoundsExceptionTest() {} + ~IndexOutOfBoundsExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testIndexOutOfBoundsException() + { + IndexOutOfBoundsException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testIndexOutOfBoundsExceptionMsg() + { + String msg = "ERROR MESSAGE"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testIndexOutOfBoundsExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testIndexOutOfBoundsExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + IndexOutOfBoundsException e(msg); + IndexOutOfBoundsException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testIndexOutOfBoundsExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + IndexOutOfBoundsException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new IndexOutOfBoundsException()", testIndexOutOfBoundsException); + RUN_TEST("new IndexOutOfBoundsException(msg)", testIndexOutOfBoundsExceptionMsg); + RUN_TEST("copy", testIndexOutOfBoundsExceptionCopy); + RUN_TEST("move", testIndexOutOfBoundsExceptionMove); + RUN_TEST("toString()", testIndexOutOfBoundsExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_runtime_exception.cpp b/modules/j/base/test/src/ut_runtime_exception.cpp new file mode 100644 index 0000000..8e3aa7b --- /dev/null +++ b/modules/j/base/test/src/ut_runtime_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class RuntimeExceptionTest : public TestCase +{ +public: + RuntimeExceptionTest() {} + ~RuntimeExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testRuntimeException() + { + RuntimeException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testRuntimeExceptionMsg() + { + String msg = "ERROR MESSAGE"; + RuntimeException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testRuntimeExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + RuntimeException e(msg); + RuntimeException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testRuntimeExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + RuntimeException e(msg); + RuntimeException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testRuntimeExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + RuntimeException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new RuntimeException()", testRuntimeException); + RUN_TEST("new RuntimeException(msg)", testRuntimeExceptionMsg); + RUN_TEST("copy", testRuntimeExceptionCopy); + RUN_TEST("move", testRuntimeExceptionMove); + RUN_TEST("toString()", testRuntimeExceptionToString); + } +}; diff --git a/modules/j/base/test/src/ut_term.cpp b/modules/j/base/test/src/ut_term.cpp new file mode 100644 index 0000000..dde7c2b --- /dev/null +++ b/modules/j/base/test/src/ut_term.cpp @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::io; +using namespace j::cppunit; + +class TermTest : public TestCase +{ +public: + TermTest() {} + ~TermTest() {} + void setUp() {} + void tearDown() {} + + void testUp() + { + std::string_view value = Term::Cursor::UP(1); + assertEquals("\x1b[1A", value.data()); + } + void testDown() + { + std::string_view value = Term::Cursor::DOWN(123); + assertEquals("\x1b[123B", value.data()); + } + void testRight() + { + std::string_view value = Term::Cursor::RIGHT(23); + assertEquals("\x1b[23C", value.data()); + } + void testLeft() + { + std::string_view value = Term::Cursor::LEFT(5); + assertEquals("\x1b[5D", value.data()); + } + void testDownLine() + { + std::string_view value = Term::Cursor::DOWN_LINE(3); + assertEquals("\x1b[3E", value.data()); + } + void testUpLine() + { + std::string_view value = Term::Cursor::UP_LINE(345); + assertEquals("\x1b[345F", value.data()); + } + void testCol() + { + std::string_view value = Term::Cursor::COL(99); + assertEquals("\x1b[99G", value.data()); + } + void testMove() + { + std::string_view value = Term::Cursor::MOVE(10, 30); + assertEquals("\x1b[10;30H", value.data()); + } + void testScroll() + { + std::string_view value = Term::Cursor::SCROLL(130); + assertEquals("\x1b[130S", value.data()); + } + void testScrollR() + { + std::string_view value = Term::Cursor::SCROLL_R(7); + assertEquals("\x1b[7T", value.data()); + } + + void suite() + { + RUN_TEST("term test UP", testUp); + RUN_TEST("term test DOWN", testDown); + RUN_TEST("term test RIGHT", testRight); + RUN_TEST("term test LEFT", testLeft); + RUN_TEST("term test DOWN_LINE", testDownLine); + RUN_TEST("term test UP_LINE", testUpLine); + RUN_TEST("term test COL", testCol); + RUN_TEST("term test MOVE", testMove); + RUN_TEST("term test SCROLL", testScroll); + RUN_TEST("term test SCROLL_R", testScrollR); + } +}; diff --git a/modules/j/base/test/src/ut_throwable.cpp b/modules/j/base/test/src/ut_throwable.cpp new file mode 100644 index 0000000..0fb21ad --- /dev/null +++ b/modules/j/base/test/src/ut_throwable.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class ThrowableChild : public Throwable +{ +public: + ThrowableChild() noexcept {}; + ~ThrowableChild() noexcept {}; + std::unique_ptr clone() const noexcept + { + return Throwable::clone(); + } +}; + +class ThrowableTest : public TestCase +{ +public: + ThrowableTest() {} + ~ThrowableTest() {} + void setUp() {} + void tearDown() {} + + void testThrowable() + { + Throwable e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testThrowableMsg() + { + String msg = "ERROR MESSAGE"; + Throwable e(msg); + assertEquals(msg, e.getMessage()); + } + + void testThrowableCopy() + { + String msg = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testThrowableMove() + { + String msg = "ERROR MESSAGE 3"; + + Throwable e(msg); + Throwable ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testThrowableToString() + { + String msg = "ERROR MESSAGE 3"; + Throwable e(msg); + assertEquals(msg, e.toString()); + } + + void testOpEq() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = e; + assertEquals(msg, ee.getMessage()); + + ee = ee; + assertEquals(msg, ee.getMessage()); + } + + void testOpEqMove() + { + String msg = "ERROR MESSAGE"; + String msg2 = "ERROR MESSAGE 2"; + + Throwable e(msg); + Throwable ee(msg2); + ee = std::move(e); + assertEquals(msg, ee.getMessage()); + + ee = std::move(ee); + assertEquals(msg, ee.getMessage()); + } + + void testClone() + { + ThrowableChild ch; + std::unique_ptr ptr = ch.clone(); + String str = ptr->toString(); + assertTrue(str.length() > 0); + } + + void suite() + { + RUN_TEST("new Throwable()", testThrowable); + RUN_TEST("new Throwable(msg)", testThrowableMsg); + RUN_TEST("copy", testThrowableCopy); + RUN_TEST("move", testThrowableMove); + RUN_TEST("toString()", testThrowableToString); + RUN_TEST("operator=(const Throwable&)", testOpEq); + RUN_TEST("operator=(const Throwable&&)", testOpEqMove); + RUN_TEST("clone", testClone); + } +}; diff --git a/modules/j/base/test/src/ut_unsupported_operation_exception.cpp b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp new file mode 100644 index 0000000..2114374 --- /dev/null +++ b/modules/j/base/test/src/ut_unsupported_operation_exception.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class UnsupportedOperationExceptionTest : public TestCase +{ +public: + UnsupportedOperationExceptionTest() {} + ~UnsupportedOperationExceptionTest() {} + void setUp() {} + void tearDown() {} + + void testUnsupportedOperationException() + { + UnsupportedOperationException e; + String exp = j::lang::Errno::message(j::lang::Errno::get()); + String act = e.getMessage(); + assertEquals(exp, act); + } + + void testUnsupportedOperationExceptionMsg() + { + String msg = "ERROR MESSAGE"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.getMessage()); + } + + void testUnsupportedOperationExceptionCopy() + { + String msg = "ERROR MESSAGE 2"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = e; + assertEquals(msg, e.getMessage()); + assertTrue(e != ee); + assertFalse(e == ee); + } + + void testUnsupportedOperationExceptionMove() + { + String msg = "ERROR MESSAGE 3"; + + UnsupportedOperationException e(msg); + UnsupportedOperationException ee = std::move(e); + assertEquals(msg, ee.getMessage()); + } + + void testUnsupportedOperationExceptionToString() + { + String msg = "ERROR MESSAGE 3"; + UnsupportedOperationException e(msg); + assertEquals(msg, e.toString()); + } + + void suite() + { + RUN_TEST("new UnsupportedOperationException()", testUnsupportedOperationException); + RUN_TEST("new UnsupportedOperationException(msg)", testUnsupportedOperationExceptionMsg); + RUN_TEST("copy", testUnsupportedOperationExceptionCopy); + RUN_TEST("move", testUnsupportedOperationExceptionMove); + RUN_TEST("toString()", testUnsupportedOperationExceptionToString); + } +}; diff --git a/modules/j/base/test/test-lib/Makefile b/modules/j/base/test/test-lib/Makefile new file mode 100644 index 0000000..b397b0f --- /dev/null +++ b/modules/j/base/test/test-lib/Makefile @@ -0,0 +1,55 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libcalc +TARGET = $(NAME).so +#TARGET = $(NAME).dll +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += +# LIBS += -L$(TOPDIR)/lib -lj-cppunit + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + + +.PHONY: utmain +utmain: + $(TOPDIR)/tool/mkutmain.sh > src/ut.cpp + diff --git a/modules/j/base/test/test-lib/libcalc.so b/modules/j/base/test/test-lib/libcalc.so new file mode 100755 index 0000000..16b52c6 --- /dev/null +++ b/modules/j/base/test/test-lib/libcalc.so Binary files differ diff --git a/modules/j/base/test/test-lib/obj/calc.d b/modules/j/base/test/test-lib/obj/calc.d new file mode 100644 index 0000000..9944f66 --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.d @@ -0,0 +1 @@ +obj/calc.o: src/calc.c diff --git a/modules/j/base/test/test-lib/obj/calc.o b/modules/j/base/test/test-lib/obj/calc.o new file mode 100644 index 0000000..4d422fc --- /dev/null +++ b/modules/j/base/test/test-lib/obj/calc.o Binary files differ diff --git a/modules/j/base/test/test-lib/src/calc.c b/modules/j/base/test/test-lib/src/calc.c new file mode 100644 index 0000000..ed54a95 --- /dev/null +++ b/modules/j/base/test/test-lib/src/calc.c @@ -0,0 +1,16 @@ +int calc_add(int v1, int v2) +{ + return (v1 + v2); +} +int calc_sub(int v1, int v2) +{ + return (v1 - v2); +} +int calc_mul(int v1, int v2) +{ + return (v1 * v2); +} +int calc_div(int v1, int v2) +{ + return (v1 / v2); +} diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile new file mode 100644 index 0000000..1c6c98f --- /dev/null +++ b/modules/j/cppunit/Makefile @@ -0,0 +1,48 @@ +# ============================================================================== +# Makefile +# ============================================================================== +# +# TOPDIR : トップディレクトリ +# RULEDIR : Meke のルール一式が格納されているディレクトリ +# NAME : モジュール名 (拡張子を含めないこと) +# TARGET : モジュールファイル名 (拡張子を含めること) +# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) +# +TOPDIR ?= ../../.. +RULEDIR ?= $(TOPDIR)/mk +NAME = libj-cppunit +TARGET = $(NAME).so +SUBDIRS = +USE_SO_VERSION = + +# ------------------------------------------------------------------------------ +# *-cmd.mk : コマンド +# *-conf.mk : 設定 +# *-auto.mk : 自動設定 +# ------------------------------------------------------------------------------ +include $(TOPDIR)/config.mk +include $(RULEDIR)/*-cmd.mk +include $(RULEDIR)/*-conf.mk +include $(RULEDIR)/*-auto.mk +# ------------------------------------------------------------------------------ +# +# 以下、オプションを適宜変更してください。 +# + +INCLUDES += -I$(TOPDIR)/include +CFLAGS += +CXXFLAGS += +LDFLAGS += +LIBS += -L$(TOPDIR)/lib -ljbase + +CLEAN_FILES += +CLEAN_DIRS += + +.DEFAULT_GOAL := all + +# ------------------------------------------------------------------------------ +# *-rule : ルール +# ------------------------------------------------------------------------------ +include $(RULEDIR)/*-rule.mk +# ------------------------------------------------------------------------------ + diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md new file mode 100644 index 0000000..dd82708 --- /dev/null +++ b/modules/j/cppunit/README.md @@ -0,0 +1,8 @@ +* Model : j.cppunit + +** includes +- j/cppunit + +** depends +- j.base + diff --git a/modules/j/cppunit/src/assert.cpp b/modules/j/cppunit/src/assert.cpp new file mode 100644 index 0000000..9ba67f9 --- /dev/null +++ b/modules/j/cppunit/src/assert.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +namespace j +{ + namespace cppunit + { + namespace Assert + { + + // オーバーロード: const char* + void _assertEqualsImpl(const char *expected, const char *actual, + const char *file, const char *func, int line) + { + if (std::strcmp(expected, actual) != 0) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << ">"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + // float, double 用 + void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, + const char *file, const char *func, int line) + { + bool isSuccess = (std::fabs(expected - actual) < delta); + if (!isSuccess) + { + std::ostringstream msg; + msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; + throw lang::AssertionError(msg.str(), file, func, line); + } + } + + void _assertTrueImpl(bool condition, const char *file, const char *func, int line) + { + if (!condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertFalseImpl(bool condition, const char *file, const char *func, int line) + { + if (condition) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + void _assertNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj != nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) + { + if (obj == nullptr) + { + throw lang::AssertionError("expected but was: ", file, func, line); + } + } + + void _failImpl(const char *file, const char *func, int line) + { + throw lang::AssertionError("fail()", file, func, line); + } + + } // namespace Assert + } // namespace cppunit +} // namespace j diff --git a/modules/j/cppunit/src/cppunit.cpp b/modules/j/cppunit/src/cppunit.cpp new file mode 100644 index 0000000..7359c93 --- /dev/null +++ b/modules/j/cppunit/src/cppunit.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +#include +#include + +using namespace j::io::Term; + +namespace j +{ + namespace cppunit + { + + //////////////////////////////////////////////////////////////////////// + // + // TestCase + // + + /** + * TestCase を構築します。 + */ + TestCase::TestCase() noexcept { /* NOP */ } + + /** + * TestCase を破棄します。 + */ + TestCase::~TestCase() noexcept { /* NOP */ } + + /** + * 各テストケース実施前に実行されます。 + */ + void TestCase::setUp() { /* NOP */ } + + /** + * 各テストケース実施後に実行されます。 + */ + void TestCase::tearDown() { /* NOP */ } + + //////////////////////////////////////////////////////////////////////// + // + // TestManager + // + + /** + * TestManager を構築します。 + */ + TestManager::TestManager() : okCount(0), ngCount(0) + { + // NOP + } + + /** + * TestManager を破棄します。 + * 破棄するタイミングで、テスト結果を出力します。 + */ + TestManager::~TestManager() + { + // NOP + } + + /** + * テスト結果を追加します。 + * + * @param file ファイル + */ + void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) + { + std::ostringstream msg; + msg << title << " [" << func << "] "; + std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; + std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; + + if (result) + { // テスト成功 + std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + okCount++; + } + else + { // テスト失敗 + std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; + ngCount++; + if (e) + { + std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; + std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; + } + } + } + + /** + * テスト結果を出力します。 + */ + void TestManager::printResult() + { + std::string line(80, '-'); + std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; + std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; + std::cout << std::endl; + } + + // テスト実施用インスタンス + TestManager testManager; + + } // namespace cppunit +} // namespace j diff --git a/tool/mkutmain.sh b/tool/mkutmain.sh new file mode 100755 index 0000000..d1fb658 --- /dev/null +++ b/tool/mkutmain.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +cat < +#include + +EOF + +# include test case files +TEST_CASE_FILES=`ls -1 src/ut_*.cpp | sed -e "s/src\///"` +for f in ${TEST_CASE_FILES}; do + echo "#include \"${f}\"" +done + +cat <