diff --git a/CMakeLists.txt b/CMakeLists.txt index 08fbe18..e47a5f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,15 +10,23 @@ if (POLICY CMP0141) # MSVC hot-reload schenanigans set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") endif() +if(POLICY CMP0092) + cmake_policy(SET CMP0092 NEW) +endif() + if(MSVC) + string(REPLACE "/EHsc" "" OLD_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${OLD_FLAGS}") string(REPLACE "/RTC1" "" OLD_FLAGS "${CMAKE_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_DEBUG "${OLD_FLAGS}") - add_compile_options(/permissive- /Zc:preprocessor /JMC /std:c++latest /GR- /GS- /EHsc-) + add_compile_options(/permissive- /Zc:preprocessor /JMC /std:c++latest /GR- /GS- /Zc:throwingNew- /EHs-) elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-ffreestanding -nostdinc++ -fno-exceptions -fno-rtti) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-ffreestanding -nostdinc++ -fno-exceptions -fno-rtti) endif() + +message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") add_subdirectory(starlib) add_subdirectory(testing) diff --git a/starlib/CMakeLists.txt b/starlib/CMakeLists.txt index 03e44d8..cf12cea 100644 --- a/starlib/CMakeLists.txt +++ b/starlib/CMakeLists.txt @@ -3,13 +3,18 @@ add_library(starlib STATIC) # Add module sources target_sources(starlib PUBLIC - FILE_SET cxx_modules TYPE CXX_MODULES FILES + FILE_SET CXX_MODULES TYPE CXX_MODULES FILES starlib.ixx process.ixx + print.ixx + native.ixx + printInternals.ixx PRIVATE starlib.cpp process.cpp - "process.ixx") + print.cpp + printInternals.cpp + native.cpp) target_include_directories(starlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/starlib/native.cpp b/starlib/native.cpp new file mode 100644 index 0000000..4230f08 --- /dev/null +++ b/starlib/native.cpp @@ -0,0 +1,27 @@ +module; +#ifdef _WIN32 +#include +#else + +#endif + +#include + +module nativeAPI; + +void* __cdecl operator new(std::size_t size) noexcept +{ + return HeapAlloc(GetProcessHeap(), 0, size); +} +void __cdecl operator delete(void* lpMemory) noexcept +{ + HeapFree(GetProcessHeap(), 0, lpMemory); +} +void* __cdecl operator new[](std::size_t size) noexcept +{ + return HeapAlloc(GetProcessHeap(), 0, size); +} +void __cdecl operator delete[](void* lpMemory) noexcept +{ + HeapFree(GetProcessHeap(), 0, lpMemory); +} \ No newline at end of file diff --git a/starlib/native.ixx b/starlib/native.ixx new file mode 100644 index 0000000..97057d1 --- /dev/null +++ b/starlib/native.ixx @@ -0,0 +1,20 @@ +// This module provides interface for native system APIs (at least tries to make it universal) + +module; + +#include +#include + +export module nativeAPI; + +static_assert(std::bit_cast(nullptr) == 0, "Null pointer is not all-zero bits. While this is conforming, this library requires the bit pattern of nullptr to be the same as the bit pattern of 0"); + +namespace starlib::detail +{ + export enum struct NativeHandle : std::uintptr_t { Null = 0 }; + export template + NativeHandle PointerToHandle(T* pointer) noexcept + { + return static_cast(reinterpret_cast>(pointer)); + } +} \ No newline at end of file diff --git a/starlib/print.cpp b/starlib/print.cpp new file mode 100644 index 0000000..d2f0c8b --- /dev/null +++ b/starlib/print.cpp @@ -0,0 +1,34 @@ +module; + +#include + +module print; + +import :internal; + +void starlib::Print(const char* szString, std::size_t dwSize) +{ + detail::PrintString(szString, dwSize); +} + +void starlib::Print(const wchar_t* szString, std::size_t dwSize) +{ + detail::PrintString(szString, dwSize); +} + +void starlib::Print(const char8_t* szString, std::size_t dwSize) +{ + const auto converted = detail::Utf8ToCP(szString, dwSize); + Print(converted.get(), dwSize); +} + +void starlib::Print(const char16_t* szString, std::size_t dwSize) +{ + const auto converted = detail::Utf16ToCP(szString, dwSize); + Print(converted.get(), dwSize); +} +void starlib::Print(const char32_t* szString, std::size_t dwSize) +{ + const auto converted = detail::Utf32ToCP(szString, dwSize); + Print(converted.get(), dwSize); +} \ No newline at end of file diff --git a/starlib/print.ixx b/starlib/print.ixx new file mode 100644 index 0000000..f5029c9 --- /dev/null +++ b/starlib/print.ixx @@ -0,0 +1,37 @@ +module; + +#include +#include +#include + +export module print; + +namespace starlib +{ + export template + concept IsCharacter = std::disjunction_v< + std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same + >; + + export void Print(const char* szString, std::size_t dwSize); + export void Print(const wchar_t* szString, std::size_t dwSize); + export void Print(const char8_t* szString, std::size_t dwSize); + export void Print(const char16_t* szString, std::size_t dwSize); + export void Print(const char32_t* szString, std::size_t dwSize); + + // templates + export template + void Print(const TChar(&string)[N]) + { + Print(string, N - 1); + } + + export void Print(std::integral auto integral) + { + + } +} \ No newline at end of file diff --git a/starlib/printInternals.cpp b/starlib/printInternals.cpp new file mode 100644 index 0000000..911403e --- /dev/null +++ b/starlib/printInternals.cpp @@ -0,0 +1,139 @@ +module; +#ifdef _WIN32 +#include +#else +// no includes here +#endif + +#include +#include +#include + +module print; + +import :internal; +import nativeAPI; + +using starlib::detail::NativeHandle; + +static NativeHandle g_hStandardOutput{}; + +void starlib::detail::InitialisePrintBuffers(void) +{ + if (g_hStandardOutput != NativeHandle::Null) + return; + +#ifdef _WIN32 + g_hStandardOutput = starlib::detail::PointerToHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + SetConsoleCP(CP_UTF8); // Ensure UTF-8 + SetConsoleOutputCP(CP_UTF8); + const auto hConsole = reinterpret_cast(std::to_underlying(g_hStandardOutput)); + + DWORD mode{}; + if (GetConsoleMode(hConsole, &mode)) + { + mode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hConsole, mode); + } +#else +#endif +} +void starlib::detail::PrintString(const char* szBuffer, std::size_t dwLength) +{ + InitialisePrintBuffers(); + +#ifdef _WIN32 + DWORD dwWritten{}; + const auto hConsole = reinterpret_cast(std::to_underlying(g_hStandardOutput)); + WriteConsoleA(hConsole, szBuffer, dwLength, &dwWritten, nullptr); +#else +#endif +} +void starlib::detail::PrintString(const wchar_t* szBuffer, std::size_t dwLength) +{ + InitialisePrintBuffers(); + +#ifdef _WIN32 + DWORD dwWritten{}; + WriteConsoleW(reinterpret_cast(std::to_underlying(g_hStandardOutput)), szBuffer, dwLength, &dwWritten, nullptr); +#else +#endif +} + +std::unique_ptr starlib::detail::Utf8ToCP(const char8_t* szInput, std::size_t& dwLength) +{ +#ifdef _WIN32 + if (szInput == nullptr || dwLength == 0) + return nullptr; + + const auto wideSize = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast(szInput), dwLength, nullptr, 0); + + std::unique_ptr wideBuffer = std::make_unique_for_overwrite(wideSize); + + wideBuffer[MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast(szInput), dwLength, wideBuffer.get(), wideSize)] = 0; + + const auto convertedSize = WideCharToMultiByte(CP_ACP, 0, wideBuffer.get(), wideSize, nullptr, 0, nullptr, nullptr); + + std::unique_ptr converted = std::make_unique_for_overwrite(convertedSize + 1); + + WideCharToMultiByte(CP_ACP, 0, wideBuffer.get(), wideSize, converted.get(), convertedSize, nullptr, nullptr); + converted[convertedSize] = 0; + dwLength = convertedSize; + return converted; +#else + #error Unsupported platform +#endif +} + +std::unique_ptr starlib::detail::Utf16ToCP(const char16_t* szInput, std::size_t& dwLength) +{ +#ifdef _WIN32 + if (szInput == nullptr || dwLength == 0) + return nullptr; + + static_assert(sizeof(wchar_t) == 2); + + const wchar_t* wideInput = reinterpret_cast(szInput); + std::unique_ptr temporaryBuffer{}; + + const int requiredSize = WideCharToMultiByte(CP_ACP, 0, wideInput, dwLength, nullptr, 0, nullptr, nullptr); + + if (requiredSize <= 0) return nullptr; + + std::unique_ptr output = std::make_unique_for_overwrite(requiredSize + 1); + WideCharToMultiByte(CP_ACP, 0, wideInput, dwLength, output.get(), requiredSize, nullptr, nullptr); + output[requiredSize] = 0; + dwLength = static_cast(requiredSize); + return output; +#else + #error Unsupported platform +#endif +} + +std::unique_ptr starlib::detail::Utf32ToCP(const char32_t* szInput, std::size_t& dwLength) +{ +#ifdef _WIN32 + std::unique_ptr utf16String = std::make_unique_for_overwrite(dwLength * 2); + std::size_t utf16Length{}; + + for (std::size_t i = 0; i < dwLength; ++i) + { + char32_t codepoint = szInput[i]; + if (codepoint <= 0xFFFF) + { + utf16String[utf16Length++] = static_cast(codepoint); + } + else if (codepoint <= 0x10FFFF) + { + codepoint -= 0x10000; + utf16String[utf16Length++] = static_cast((codepoint >> 10) + 0xD800); + utf16String[utf16Length++] = static_cast((codepoint & 0x3FF) + 0xDC00); + } + else return nullptr; + } + dwLength = utf16Length; + return starlib::detail::Utf16ToCP(utf16String.get(), dwLength); +#else + #error Unsupported platform +#endif +} \ No newline at end of file diff --git a/starlib/printInternals.ixx b/starlib/printInternals.ixx new file mode 100644 index 0000000..a0b9e22 --- /dev/null +++ b/starlib/printInternals.ixx @@ -0,0 +1,17 @@ +module; + +#include +#include + +export module print:internal; + +namespace starlib::detail +{ + void InitialisePrintBuffers(void); + export void PrintString(const char* szBuffer, std::size_t dwLength); + export void PrintString(const wchar_t* szBuffer, std::size_t dwLength); + + export std::unique_ptr Utf8ToCP(const char8_t* szInput, std::size_t& dwLength); + export std::unique_ptr Utf16ToCP(const char16_t* szInput, std::size_t& dwLength); + export std::unique_ptr Utf32ToCP(const char32_t* szInput, std::size_t& dwLength); +} \ No newline at end of file diff --git a/starlib/starlib.cpp b/starlib/starlib.cpp index eb7e596..5e3eafe 100644 --- a/starlib/starlib.cpp +++ b/starlib/starlib.cpp @@ -2,6 +2,6 @@ module starlib; extern "C" void DefaultMain(void) { - starlib::process::Exit(1); + starlib::process::Exit(Program::Main()); } diff --git a/starlib/starlib.ixx b/starlib/starlib.ixx index 61d3048..9a638d6 100644 --- a/starlib/starlib.ixx +++ b/starlib/starlib.ixx @@ -1,3 +1,10 @@ export module starlib; export import process; +export import print; + +export class Program +{ +public: + static int Main(void); +}; \ No newline at end of file diff --git a/testing/main.cpp b/testing/main.cpp index 934bbc1..ea6d8e4 100644 --- a/testing/main.cpp +++ b/testing/main.cpp @@ -1,2 +1,11 @@ import starlib; +int Program::Main(void) +{ + starlib::Print("Meow ordinary character\n"); + starlib::Print(L"Meow wide character\n"); + starlib::Print(u8"Meow UTF-8\n"); + starlib::Print(u"Meow UTF-16\n"); + starlib::Print(U"Meow UTF-32\n"); + return 0; +} \ No newline at end of file