diff --git a/Src/IACore/CMakeLists.txt b/Src/IACore/CMakeLists.txt
index ed627be..8be49bf 100644
--- a/Src/IACore/CMakeLists.txt
+++ b/Src/IACore/CMakeLists.txt
@@ -1,5 +1,6 @@
set(SRC_FILES
"imp/cpp/IACore.cpp"
+ "imp/cpp/Logger.cpp"
)
add_library(IACore STATIC ${SRC_FILES})
diff --git a/Src/IACore/imp/cpp/Logger.cpp b/Src/IACore/imp/cpp/Logger.cpp
new file mode 100644
index 0000000..4747e8b
--- /dev/null
+++ b/Src/IACore/imp/cpp/Logger.cpp
@@ -0,0 +1,61 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include
+#include
+
+namespace IACore
+{
+ Logger::ELogLevel Logger::s_logLevel{Logger::ELogLevel::WARN};
+ HRTimePoint Logger::s_startTime{};
+ IACore::File *Logger::s_logFile{};
+
+ VOID Logger::Initialize()
+ {
+ s_startTime = HRClock::now();
+ }
+
+ VOID Logger::Terminate()
+ {
+ if (s_logFile)
+ {
+ s_logFile->Close();
+ delete s_logFile;
+ }
+ }
+
+ BOOL Logger::EnableLoggingToDisk(IN PCCHAR filePath)
+ {
+ if (s_logFile)
+ return true;
+ s_logFile = new IACore::File(filePath, IACore::File::EOpenFlags::Write);
+ return s_logFile->IsOpen();
+ }
+
+ VOID Logger::SetLogLevel(IN ELogLevel logLevel)
+ {
+ s_logLevel = logLevel;
+ }
+
+ VOID Logger::LogInternal(IN PCCHAR prefix, IN String&& msg)
+ {
+ std::chrono::duration elapsed = HRClock::now() - s_startTime;
+ double timestamp = elapsed.count();
+ const auto outLine = std::format("[{:>8.3f}]: [{}]: {}\n", timestamp, prefix, msg);
+ const auto outStream = s_logFile ? s_logFile->GetStreamHandle() : &std::cout;
+ outStream->write(outLine.data(), outLine.size());
+ }
+}
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/File.hpp b/Src/IACore/inc/IACore/File.hpp
index 0438918..efca1b6 100644
--- a/Src/IACore/inc/IACore/File.hpp
+++ b/Src/IACore/inc/IACore/File.hpp
@@ -1,16 +1,16 @@
-// IACore-OSS; The Core Library for All IA Open Source Projects
+// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev)
-//
+//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
-//
+//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
-//
+//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
@@ -21,47 +21,54 @@
#include
#include
-namespace IACore {
+namespace IACore
+{
namespace fs = std::filesystem;
- class File {
- public:
+ class File
+ {
+ public:
// Modern mapping of flags to standard IO streams
- enum class EOpenFlags : UINT32 {
- Read = 1 << 0, // std::ios::in
- Write = 1 << 1, // std::ios::out
+ enum class EOpenFlags : UINT32
+ {
+ Read = 1 << 0, // std::ios::in
+ Write = 1 << 1, // std::ios::out
Binary = 1 << 2, // std::ios::binary
Append = 1 << 3, // std::ios::app
- Trunc = 1 << 4 // std::ios::trunc
+ Trunc = 1 << 4 // std::ios::trunc
};
// ---------------------------------------------------------------------
// Static Helper API (The "One-Liners")
// ---------------------------------------------------------------------
-
+
// Reads entire file into a binary vector
NO_DISCARD("Handle the error")
- STATIC tl::expected, String> ReadToVector(CONST String& path) {
+
+ STATIC tl::expected, String> ReadToVector(CONST String &path)
+ {
// 1. Check File Existence & Size
std::error_code ec;
auto fileSize = fs::file_size(path, ec);
-
- if (ec) {
+
+ if (ec)
+ {
return tl::make_unexpected(String("File not found or inaccessible: ") + path);
}
// 2. Open Stream
std::ifstream file(path, std::ios::binary);
- if (!file.is_open()) {
+ if (!file.is_open())
+ {
return tl::make_unexpected(String("Failed to open file handle: ") + path);
}
// 3. Read
Vector buffer;
buffer.resize(CAST(fileSize, SIZE_T));
- file.read(REINTERPRET(buffer.data(), char*), fileSize);
- if(file.fail())
+ file.read(REINTERPRET(buffer.data(), char *), fileSize);
+ if (file.fail())
return tl::make_unexpected(String("Read error: ") + path);
return buffer;
@@ -69,10 +76,13 @@ namespace IACore {
// Reads entire file into a String
NO_DISCARD("Handle the error")
- STATIC tl::expected ReadToString(CONST String& path) {
+
+ STATIC tl::expected ReadToString(CONST String &path)
+ {
// Reuse the binary logic to avoid code duplication, then cast
auto result = ReadToVector(path);
- if (!result) {
+ if (!result)
+ {
return tl::make_unexpected(result.error());
}
@@ -89,86 +99,117 @@ namespace IACore {
// Old: ExtractFilenameFromPath
// New: true -> filename(), false -> stem()
- template
- STATIC String ExtractFilename(CONST String& pathStr) {
+ template STATIC String ExtractFilename(CONST String &pathStr)
+ {
fs::path p(pathStr);
- if CONSTEXPR (includeExtension) {
+ if CONSTEXPR (includeExtension)
+ {
return p.filename().string(); // "sprite.png"
- } else {
- return p.stem().string(); // "sprite"
+ }
+ else
+ {
+ return p.stem().string(); // "sprite"
}
}
// Old: ExtractDirectoryFromPath
- STATIC String ExtractDirectory(CONST String& pathStr) {
+ STATIC String ExtractDirectory(CONST String &pathStr)
+ {
fs::path p(pathStr);
return p.parent_path().string(); // "assets/textures"
}
- STATIC BOOL Exists(CONST String& pathStr) {
+ STATIC BOOL Exists(CONST String &pathStr)
+ {
std::error_code ec;
return fs::exists(pathStr, ec);
}
- public:
+ public:
// ---------------------------------------------------------------------
// Instance API (For streaming/partial reads)
// ---------------------------------------------------------------------
-
+
File() = default;
- // RAII Constructor
- File(CONST String& path, UINT32 flags) {
+#ifdef __cplusplus
+ File(CONST String &path, EOpenFlags flags)
+#else
+ File(CONST String &path, UINT32 flags)
+#endif
+ {
UNUSED(Open(path, flags));
}
- ~File() {
+ ~File()
+ {
Close();
}
- // Returns void (success) or error String
- tl::expected Open(CONST String& path, UINT32 flags) {
+#ifdef __cplusplus
+ tl::expected Open(CONST String &path, EOpenFlags flags)
+#else
+ tl::expected Open(CONST String &path, UINT32 flags)
+#endif
+ {
Close(); // Ensure previous handle is closed
std::ios_base::openmode mode = static_cast(0);
- if (flags & (UINT32)EOpenFlags::Read) mode |= std::ios::in;
- if (flags & (UINT32)EOpenFlags::Write) mode |= std::ios::out;
- if (flags & (UINT32)EOpenFlags::Binary) mode |= std::ios::binary;
- if (flags & (UINT32)EOpenFlags::Append) mode |= std::ios::app;
- if (flags & (UINT32)EOpenFlags::Trunc) mode |= std::ios::trunc;
+ if ((UINT32) flags & (UINT32) EOpenFlags::Read)
+ mode |= std::ios::in;
+ if ((UINT32) flags & (UINT32) EOpenFlags::Write)
+ mode |= std::ios::out;
+ if ((UINT32) flags & (UINT32) EOpenFlags::Binary)
+ mode |= std::ios::binary;
+ if ((UINT32) flags & (UINT32) EOpenFlags::Append)
+ mode |= std::ios::app;
+ if ((UINT32) flags & (UINT32) EOpenFlags::Trunc)
+ mode |= std::ios::trunc;
m_fs.open(path, mode);
- if (!m_fs.is_open()) {
+ if (!m_fs.is_open())
+ {
return tl::make_unexpected(String("Failed to open file: ") + path);
}
return {};
}
- VOID Close() {
- if (m_fs.is_open()) {
+ VOID Close()
+ {
+ if (m_fs.is_open())
+ {
m_fs.close();
}
}
- BOOL IsOpen() CONST {
+ BOOL IsOpen() CONST
+ {
return m_fs.is_open();
}
// Returns number of bytes read
- SIZE_T Read(PVOID buffer, SIZE_T size) {
- if (!m_fs.is_open()) return 0;
- m_fs.read(REINTERPRET(buffer, char*), size);
+ SIZE_T Read(PVOID buffer, SIZE_T size)
+ {
+ if B_UNLIKELY (!m_fs.is_open())
+ return 0;
+ m_fs.read(REINTERPRET(buffer, char *), size);
return static_cast(m_fs.gcount());
}
- VOID Write(CONST PVOID buffer, SIZE_T size) {
- if (m_fs.is_open()) {
- m_fs.write(REINTERPRET(buffer, const char*), size);
- }
+ VOID Write(CONST PVOID buffer, SIZE_T size)
+ {
+ if B_UNLIKELY (!m_fs.is_open())
+ return;
+ m_fs.write(REINTERPRET(buffer, const char *), size);
}
- private:
+ std::fstream *GetStreamHandle()
+ {
+ return &m_fs;
+ }
+
+ private:
std::fstream m_fs;
};
-}
\ No newline at end of file
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/IACore.hpp b/Src/IACore/inc/IACore/IACore.hpp
index 59aa189..79f19f1 100644
--- a/Src/IACore/inc/IACore/IACore.hpp
+++ b/Src/IACore/inc/IACore/IACore.hpp
@@ -22,29 +22,6 @@
namespace IACore
{
- template
- VOID LogInfo(Args... args)
- {
- StringStream ss;
- UNUSED((ss << ... << args));
- printf( __CC_WHITE "[INFO]: %s" __CC_DEFAULT "\n", ss.str().c_str());
- }
-
- template
- VOID LogWarn(Args... args)
- {
- StringStream ss;
- UNUSED((ss << ... << args));
- printf( __CC_YELLOW "[WARN]: %s" __CC_DEFAULT "\n", ss.str().c_str());
- }
-
- template
- VOID LogError(Args... args)
- {
- StringStream ss;
- UNUSED((ss << ... << args));
- printf( __CC_RED "[ERROR]: %s" __CC_DEFAULT "\n", ss.str().c_str());
- }
} // namespace IACore
#endif
diff --git a/Src/IACore/inc/IACore/Logger.hpp b/Src/IACore/inc/IACore/Logger.hpp
new file mode 100644
index 0000000..d496026
--- /dev/null
+++ b/Src/IACore/inc/IACore/Logger.hpp
@@ -0,0 +1,117 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+#include
+
+namespace IACore
+{
+ class File;
+
+ class Logger
+ {
+ public:
+ enum class ELogLevel
+ {
+ VERBOSE,
+ INFO,
+ WARN,
+ ERROR
+ };
+
+ public:
+ STATIC VOID Initialize();
+ STATIC VOID Terminate();
+
+ STATIC BOOL EnableLoggingToDisk(IN PCCHAR filePath);
+ STATIC VOID SetLogLevel(IN ELogLevel logLevel);
+
+ template STATIC VOID Status(FormatterString fmt, Args &&...args)
+ {
+ LogStatus(std::vformat(fmt.get(), std::make_format_args(args...)));
+ }
+
+ template STATIC VOID Info(FormatterString fmt, Args &&...args)
+ {
+ LogInfo(std::vformat(fmt.get(), std::make_format_args(args...)));
+ }
+
+ template STATIC VOID Warn(FormatterString fmt, Args &&...args)
+ {
+ LogWarn(std::vformat(fmt.get(), std::make_format_args(args...)));
+ }
+
+ template STATIC VOID Error(FormatterString fmt, Args &&...args)
+ {
+ LogError(std::vformat(fmt.get(), std::make_format_args(args...)));
+ }
+
+ private:
+#if IA_ENABLE_LOGGING > 0
+ STATIC VOID LogStatus(IN String &&msg)
+ {
+ if (s_logLevel <= ELogLevel::VERBOSE)
+ LogInternal("STATUS", IA_MOVE(msg));
+ }
+
+ STATIC VOID LogInfo(IN String &&msg)
+ {
+ if (s_logLevel <= ELogLevel::INFO)
+ LogInternal("INFO", IA_MOVE(msg));
+ }
+
+ STATIC VOID LogWarn(IN String &&msg)
+ {
+ if (s_logLevel <= ELogLevel::WARN)
+ LogInternal("WARN", IA_MOVE(msg));
+ }
+
+ STATIC VOID LogError(IN String &&msg)
+ {
+ if (s_logLevel <= ELogLevel::ERROR)
+ LogInternal("ERROR", IA_MOVE(msg));
+ }
+#else
+ STATIC VOID LogStatus(IN String &&msg)
+ {
+ UNUSED(msg);
+ }
+
+ STATIC VOID LogInfo(IN String &&msg)
+ {
+ UNUSED(msg);
+ }
+
+ STATIC VOID LogWarn(IN String &&msg)
+ {
+ UNUSED(msg);
+ }
+
+ STATIC VOID LogError(IN String &&msg)
+ {
+ UNUSED(msg);
+ }
+#endif
+
+ STATIC VOID LogInternal(IN PCCHAR prefix, IN String &&msg);
+
+ private:
+ STATIC ELogLevel s_logLevel;
+ STATIC HRTimePoint s_startTime;
+ STATIC IACore::File *s_logFile;
+ };
+}
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/PCH.hpp b/Src/IACore/inc/IACore/PCH.hpp
index 8ab2850..efadcb9 100644
--- a/Src/IACore/inc/IACore/PCH.hpp
+++ b/Src/IACore/inc/IACore/PCH.hpp
@@ -29,6 +29,8 @@
# include
# include
# include
+# include
+# include
# include
# include
# include
@@ -526,11 +528,9 @@ using String = std::string;
using StringView = std::string_view;
using StringStream = std::stringstream;
-template STATIC String BuildString(Args &&...args)
-{
- std::stringstream ss;
- (ss << ... << args);
- return ss.str();
-}
+using HRClock = std::chrono::high_resolution_clock;
+using HRTimePoint = std::chrono::time_point;
+
+template using FormatterString = std::format_string;
#endif