diff --git a/.clang-format b/.clang-format index 849cdca54..24420da65 100644 --- a/.clang-format +++ b/.clang-format @@ -4,31 +4,31 @@ BasedOnStyle: LLVM Language: Cpp AlignAfterOpenBracket: Align AlignArrayOfStructures: Left -AlignConsecutiveAssignments: 'true' -AlignConsecutiveDeclarations: 'true' +AlignConsecutiveAssignments: "true" +AlignConsecutiveDeclarations: "true" AlignEscapedNewlines: Left AlignOperands: Align -AlignTrailingComments: 'true' -AllowAllParametersOfDeclarationOnNextLine: 'false' -AllowAllArgumentsOnNextLine: 'false' -AllowShortBlocksOnASingleLine: 'true' -AllowShortCaseLabelsOnASingleLine: 'true' +AlignTrailingComments: "true" +AllowAllParametersOfDeclarationOnNextLine: "false" +AllowAllArgumentsOnNextLine: "false" +AllowShortBlocksOnASingleLine: "true" +AllowShortCaseLabelsOnASingleLine: "true" AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: Always -AllowShortLoopsOnASingleLine: 'true' -AllowShortLambdasOnASingleLine: 'true' -AllowShortEnumsOnASingleLine: 'false' +AllowShortLoopsOnASingleLine: "true" +AllowShortLambdasOnASingleLine: "true" +AllowShortEnumsOnASingleLine: "false" AlignConsecutiveMacros: Consecutive BitFieldColonSpacing: After -BreakBeforeConceptDeclarations: 'true' +BreakBeforeConceptDeclarations: "true" BreakBinaryOperations: RespectPrecedence EmptyLineBeforeAccessModifier: LogicalBlock EnumTrailingComma: Insert -FixNamespaceComments: 'true' +FixNamespaceComments: "true" IndentExternBlock: Indent -IndentGotoLabels: 'false' -IndentRequiresClause: 'true' -InsertBraces: 'false' +IndentGotoLabels: "false" +IndentRequiresClause: "true" +InsertBraces: "false" InsertTrailingCommas: Wrapped LambdaBodyIndentation: Signature PPIndentWidth: 4 @@ -36,20 +36,29 @@ PenaltyBreakAssignment: 1000 PenaltyReturnTypeOnItsOwnLine: 1000 PenaltyBreakBeforeFirstCallParameter: 1000 PenaltyBreakOpenParenthesis: 1000 -PenaltyBreakBeforeMemberAccess: 10 -PenaltyIndentedWhitespace: 1 +PenaltyBreakBeforeMemberAccess: 1000 +PenaltyIndentedWhitespace: 2 ContinuationIndentWidth: 2 ReferenceAlignment: Left # RemoveBracesLLVM: 'true' RequiresClausePosition: OwnLine SeparateDefinitionBlocks: Always -SpaceAfterLogicalNot: 'false' +SpaceAfterLogicalNot: "false" SpaceAroundPointerQualifiers: After -SpaceBeforeCaseColon: 'false' +SpaceBeforeCaseColon: "false" AlwaysBreakAfterDefinitionReturnType: None -SpaceBeforeSquareBrackets: 'false' -SpaceInEmptyBlock: 'false' -AttributeMacros: [STORMKIT_FORCE_INLINE, STORMKIT_API, STORMKIT_PRIVATE, STORMKIT_PUBLIC, STORMKIT_CONST, STORMKIT_PURE, STORMKIT_INTRINSIC] +SpaceBeforeSquareBrackets: "false" +SpaceInEmptyBlock: "false" +AttributeMacros: + [ + STORMKIT_FORCE_INLINE, + STORMKIT_API, + STORMKIT_PRIVATE, + STORMKIT_PUBLIC, + STORMKIT_CONST, + STORMKIT_PURE, + STORMKIT_INTRINSIC, + ] IfMacros: [CASE_DO, CASE_DO_RETURN, CASE, CASE_ARGS_DO] BreakAfterAttributes: Always Macros: @@ -60,120 +69,122 @@ Macros: - STORMKIT_CONST=[[maybe_unused]] - STORMKIT_PURE=[[maybe_unused]] - STORMKIT_INTRINSIC=[[maybe_unused]] - + QualifierAlignment: Custom QualifierOrder: [static, inline, volatile, restrict, constexpr, const, type] SpacesInLineCommentPrefix: - Minimum: '1' - Maximum: '4' + Minimum: "1" + Maximum: "4" AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: 'false' -AlwaysBreakTemplateDeclarations: 'Yes' -BinPackArguments: 'false' -BinPackParameters: 'false' +AlwaysBreakBeforeMultilineStrings: "false" +AlwaysBreakTemplateDeclarations: "Yes" +BinPackArguments: "false" +BinPackParameters: "false" BreakBeforeBinaryOperators: All BreakBeforeBraces: Attach BreakConstructorInitializers: BeforeColon +BreakBeforeCloseBracketBracedList: "true" BreakInheritanceList: BeforeColon -BreakStringLiterals: 'true' -ColumnLimit: '130' -CompactNamespaces: 'true' -ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' -ConstructorInitializerIndentWidth: '4' +BreakStringLiterals: "true" +ColumnLimit: "130" +CompactNamespaces: "true" +ConstructorInitializerAllOnOneLineOrOnePerLine: "true" +ConstructorInitializerIndentWidth: "4" IncludeBlocks: Preserve -IndentCaseLabels: 'true' -IndentWidth: '4' -IndentWrappedFunctionNames: 'true' +IndentCaseLabels: "true" +IndentWidth: "4" +IndentWrappedFunctionNames: "true" IndentPPDirectives: BeforeHash -KeepEmptyLinesAtTheStartOfBlocks: 'false' -Cpp11BracedListStyle: 'false' -MaxEmptyLinesToKeep: '1' +KeepEmptyLinesAtTheStartOfBlocks: "false" +Cpp11BracedListStyle: "Block" +MaxEmptyLinesToKeep: "1" NamespaceIndentation: All ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: false PointerAlignment: Left -SortIncludes: 'true' -SortUsingDeclarations: 'true' -SpaceAfterTemplateKeyword: 'false' -SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeCpp11BracedList: 'true' -SpaceBeforeCtorInitializerColon: 'true' -SpaceBeforeInheritanceColon: 'false' -SpaceBeforeRangeBasedForLoopColon: 'true' -SpaceInEmptyParentheses: 'false' -SpacesInAngles: 'false' +SortIncludes: "true" +SortUsingDeclarations: "true" +SpaceAfterTemplateKeyword: "false" +SpaceBeforeAssignmentOperators: "true" +SpaceBeforeCpp11BracedList: "true" +SpaceBeforeCtorInitializerColon: "true" +SpaceBeforeInheritanceColon: "false" +SpaceBeforeRangeBasedForLoopColon: "true" +SpaceInEmptyParentheses: "false" +SpacesInAngles: "false" Standard: c++20 -TabWidth: '4' +TabWidth: "4" UseTab: Never --- Language: ObjC AlignAfterOpenBracket: Align AlignArrayOfStructures: Left -AlignConsecutiveAssignments: 'true' -AlignConsecutiveDeclarations: 'false' +AlignConsecutiveAssignments: "true" +AlignConsecutiveDeclarations: "false" AlignEscapedNewlines: Left AlignOperands: Align -AlignTrailingComments: 'true' -AllowAllParametersOfDeclarationOnNextLine: 'false' -AllowAllArgumentsOnNextLine: 'false' -AllowShortBlocksOnASingleLine: 'true' -AllowShortCaseLabelsOnASingleLine: 'true' +AlignTrailingComments: "true" +AllowAllParametersOfDeclarationOnNextLine: "false" +AllowAllArgumentsOnNextLine: "false" +AllowShortBlocksOnASingleLine: "true" +AllowShortCaseLabelsOnASingleLine: "true" AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: Always -AllowShortLoopsOnASingleLine: 'true' -AllowShortLambdasOnASingleLine: 'true' +AllowShortLoopsOnASingleLine: "true" +AllowShortLambdasOnASingleLine: "true" AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: 'false' -AlwaysBreakTemplateDeclarations: 'Yes' -BinPackArguments: 'false' -BinPackParameters: 'false' +AlwaysBreakBeforeMultilineStrings: "false" +AlwaysBreakTemplateDeclarations: "Yes" +BinPackArguments: "false" +BinPackParameters: "false" BreakBeforeBinaryOperators: All BreakBeforeBraces: Attach BreakBinaryOperations: OnePerLine BreakConstructorInitializers: BeforeColon BreakInheritanceList: BeforeColon -BreakStringLiterals: 'true' -ColumnLimit: '130' -CompactNamespaces: 'true' -ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' -ConstructorInitializerIndentWidth: '4' -ContinuationIndentWidth: '4' +BreakStringLiterals: "true" +ColumnLimit: "130" +CompactNamespaces: "true" +ConstructorInitializerAllOnOneLineOrOnePerLine: "false" +ConstructorInitializerIndentWidth: "4" +ContinuationIndentWidth: "4" IncludeBlocks: Preserve -IndentCaseLabels: 'true' -IndentWidth: '4' -IndentWrappedFunctionNames: 'true' +IndentCaseLabels: "true" +IndentWidth: "4" +IndentWrappedFunctionNames: "true" IndentPPDirectives: BeforeHash -KeepEmptyLinesAtTheStartOfBlocks: 'false' -Cpp11BracedListStyle: 'false' -MaxEmptyLinesToKeep: '1' +KeepEmptyLinesAtTheStartOfBlocks: "false" +Cpp11BracedListStyle: "false" +MaxEmptyLinesToKeep: "1" NamespaceIndentation: All ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: false PointerAlignment: Right -PenaltyBreakAssignment: 1000 -PenaltyReturnTypeOnItsOwnLine: 1000 -PenaltyBreakBeforeFirstCallParameter: 1000 -PenaltyBreakOpenParenthesis: 1000 -PenaltyBreakBeforeMemberAccess: 10 -SortIncludes: 'true' -SortUsingDeclarations: 'true' -SpaceAfterTemplateKeyword: 'false' -SpaceBeforeAssignmentOperators: 'true' -SpaceBeforeCpp11BracedList: 'true' -SpaceBeforeCtorInitializerColon: 'true' -SpaceBeforeInheritanceColon: 'false' -SpaceBeforeRangeBasedForLoopColon: 'true' -SpaceInEmptyParentheses: 'false' -SpacesInAngles: 'false' +PenaltyBreakAssignment: 300 +PenaltyReturnTypeOnItsOwnLine: 100 +PenaltyBreakBeforeFirstCallParameter: 500 +PenaltyBreakOpenParenthesis: 700 +PenaltyBreakBeforeMemberAccess: 700 +SortIncludes: "true" +SortUsingDeclarations: "true" +SpaceAfterTemplateKeyword: "false" +SpaceBeforeAssignmentOperators: "true" +SpaceBeforeCpp11BracedList: "true" +SpaceBeforeCtorInitializerColon: "true" +SpaceBeforeInheritanceColon: "false" +SpaceBeforeRangeBasedForLoopColon: "true" +SpaceInEmptyParentheses: "false" +SpacesInAngles: "false" Standard: c++20 -TabWidth: '4' +TabWidth: "4" UseTab: Never -PenaltyIndentedWhitespace: '1' +PenaltyIndentedWhitespace: "1" QualifierAlignment: Custom QualifierOrder: [static, inline, volatile, restrict, constexpr, const, type] -AttributeMacros: [STORMKIT_FORCE_INLINE, STORMKIT_API, STORMKIT_PRIVATE, STORMKIT_PUBLIC] +AttributeMacros: + [STORMKIT_FORCE_INLINE, STORMKIT_API, STORMKIT_PRIVATE, STORMKIT_PUBLIC] IfMacros: [CASE_DO, CASE_DO_RETURN, CASE, CASE_ARGS_DO] BreakAfterAttributes: Always diff --git a/examples/wsi/events/win32/manifest.manifest b/examples/common/Windows/manifest.manifest similarity index 100% rename from examples/wsi/events/win32/manifest.manifest rename to examples/common/Windows/manifest.manifest diff --git a/examples/log/file-logger/iOS/info.plist b/examples/common/macOS/info.plist similarity index 97% rename from examples/log/file-logger/iOS/info.plist rename to examples/common/macOS/info.plist index 83e13e582..6e96e13c8 100644 --- a/examples/log/file-logger/iOS/info.plist +++ b/examples/common/macOS/info.plist @@ -1,44 +1,44 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + LSRequiresIPhoneOS + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + + diff --git a/examples/entities/gameoflife/src/App.cpp b/examples/entities/gameoflife/src/App.cpp index 97f47c4b6..d1545f474 100644 --- a/examples/entities/gameoflife/src/App.cpp +++ b/examples/entities/gameoflife/src/App.cpp @@ -25,7 +25,7 @@ App::~App() { ilog("Cleaning"); } -auto App::run([[maybe_unused]] const int argc, [[maybe_unused]] CZString argv[]) -> i32 { +auto App::run([[maybe_unused]] const int argc, [[maybe_unused]] czstring argv[]) -> i32 { using Clock = std::chrono::high_resolution_clock; using namespace stormkit::literals; @@ -83,7 +83,7 @@ auto App::run([[maybe_unused]] const int argc, [[maybe_unused]] CZString argv[]) auto App::do_initWindow() -> void { const auto window_style = wsi::WindowStyle::ALL; - m_window = allocate(WINDOW_TITLE, math::ExtentU { 800u, 600u }, window_style); + m_window = allocate(WINDOW_TITLE, math::uextent2 { 800u, 600u }, window_style); m_renderer = allocate(*m_window); } @@ -124,8 +124,8 @@ auto App::handleKeyboard(const stormkit::wsi::KeyReleasedEventData& event) -> vo m_is_on_edit_mode = !m_is_on_edit_mode; m_update_system->setEditModeEnabled(m_is_on_edit_mode); break; - case wsi::Key::ADD: m_update_system->incrementDelta(Secondf { 0.01f }); break; - case wsi::Key::SUBSTRACT: m_update_system->incrementDelta(Secondf { -0.01f }); break; + case wsi::Key::ADD: m_update_system->incrementDelta(fsecond { 0.01f }); break; + case wsi::Key::SUBSTRACT: m_update_system->incrementDelta(fsecond { -0.01f }); break; default: break; } } @@ -135,11 +135,11 @@ auto App::handleMouse(const stormkit::wsi::MouseButtonPushedEventData& event) -> return; if (event.button != wsi::MouseButton::LEFT) return; - const auto cell_width = as(m_window->size().width) / as(BOARD_SIZE); - const auto cell_height = as(m_window->size().height) / as(BOARD_SIZE); + const auto cell_width = as(m_window->size().width) / as(BOARD_SIZE); + const auto cell_height = as(m_window->size().height) / as(BOARD_SIZE); - const auto x = glm::floor(as(event.position.x) / cell_width); - const auto y = glm::floor(as(event.position.y) / cell_height); + const auto x = glm::floor(as(event.position.x) / cell_width); + const auto y = glm::floor(as(event.position.y) / cell_height); const auto cells = m_entities.entities_with_component(); const auto it = std::ranges::find_if(cells, [&](const auto e) { diff --git a/examples/entities/gameoflife/src/App.mpp b/examples/entities/gameoflife/src/App.cppm similarity index 90% rename from examples/entities/gameoflife/src/App.mpp rename to examples/entities/gameoflife/src/App.cppm index 0c76586fc..ef52d2ecd 100644 --- a/examples/entities/gameoflife/src/App.mpp +++ b/examples/entities/gameoflife/src/App.cppm @@ -1,59 +1,59 @@ - - -#ifdef STORMKIT_BUILD_MODULES -export module App; - -import std; - -import stormkit.core; -import stormkit.image; -import stormkit.wsi; -import stormkit.entities; - -import Renderer; - -export { -#else - #include - - #include - #include - #include - #include - - #include "Renderer.mpp" -#endif - - class UpdateBoardSystem; - - class App: public stormkit::App { - public: - App(); - ~App() override; - - auto run(const int argc, CZString argv[]) -> stormkit::i32 override; - - private: - auto do_initWindow() -> void; - - auto handleKeyboard(const stormkit::wsi::KeyReleasedEventData& event) -> void; - auto handleMouse(const stormkit::wsi::MouseButtonPushedEventData& event) -> void; - auto createCell(stormkit::u32 x, stormkit::u32 y) -> void; - - std::unique_ptr m_window; - - bool m_fullscreen_enabled = false; - bool m_is_on_edit_mode = true; - - std::unique_ptr m_renderer; - - stormkit::entities::EntityManager m_entities; - - stormkit::image::Image m_board; - - UpdateBoardSystem* m_update_system = nullptr; - }; - -#ifdef STORMKIT_BUILD_MODULES -} -#endif + + +#ifdef STORMKIT_BUILD_MODULES +export module App; + +import std; + +import stormkit.core; +import stormkit.image; +import stormkit.wsi; +import stormkit.entities; + +import Renderer; + +export { +#else + #include + + #include + #include + #include + #include + + #include "Renderer.mpp" +#endif + + class UpdateBoardSystem; + + class App: public stormkit::App { + public: + App(); + ~App() override; + + auto run(const int argc, czstring argv[]) -> stormkit::i32 override; + + private: + auto do_initWindow() -> void; + + auto handleKeyboard(const stormkit::wsi::KeyReleasedEventData& event) -> void; + auto handleMouse(const stormkit::wsi::MouseButtonPushedEventData& event) -> void; + auto createCell(stormkit::u32 x, stormkit::u32 y) -> void; + + std::unique_ptr m_window; + + bool m_fullscreen_enabled = false; + bool m_is_on_edit_mode = true; + + std::unique_ptr m_renderer; + + stormkit::entities::EntityManager m_entities; + + stormkit::image::Image m_board; + + UpdateBoardSystem* m_update_system = nullptr; + }; + +#ifdef STORMKIT_BUILD_MODULES +} +#endif diff --git a/examples/entities/gameoflife/src/Components.mpp b/examples/entities/gameoflife/src/Components.cppm similarity index 77% rename from examples/entities/gameoflife/src/Components.mpp rename to examples/entities/gameoflife/src/Components.cppm index 290615e1f..cfd9c7265 100644 --- a/examples/entities/gameoflife/src/Components.mpp +++ b/examples/entities/gameoflife/src/Components.cppm @@ -1,28 +1,28 @@ - - -#ifdef STORMKIT_BUILD_MODULES -export module Components; - -import std; - -import stormkit.core; -import stormkit.entities; - -export { -#else - #include - - #include - #include -#endif - - struct PositionComponent: public stormkit::entities::Component { - stormkit::u32 x; - stormkit::u32 y; - - static constexpr Type TYPE = stormkit::entities::component_hash("PositionComponent"); - }; - -#ifdef STORMKIT_BUILD_MODULES -} -#endif + + +#ifdef STORMKIT_BUILD_MODULES +export module Components; + +import std; + +import stormkit.core; +import stormkit.entities; + +export { +#else + #include + + #include + #include +#endif + + struct PositionComponent: public stormkit::entities::Component { + stormkit::u32 x; + stormkit::u32 y; + + static constexpr Type TYPE = stormkit::hash("PositionComponent"); + }; + +#ifdef STORMKIT_BUILD_MODULES +} +#endif diff --git a/examples/entities/gameoflife/src/Constants.mpp b/examples/entities/gameoflife/src/Constants.cppm similarity index 82% rename from examples/entities/gameoflife/src/Constants.mpp rename to examples/entities/gameoflife/src/Constants.cppm index 1b5a5fad4..ca6ac6433 100644 --- a/examples/entities/gameoflife/src/Constants.mpp +++ b/examples/entities/gameoflife/src/Constants.cppm @@ -1,54 +1,54 @@ -// Copyright (C) 2024 Arthur LAURENT -// This file is subject to the license terms in the LICENSE file -// found in the top-level of this distribution - -#ifdef STORMKIT_BUILD_MODULES -export module Constants; - -import std; - -import stormkit.core; -import stormkit.log; -import stormkit.gpu; - -export { -#else - #include - - #include - #include - #include -#endif - - inline constexpr auto VERTEX_SIZE - = sizeof(stormkit::math::vec2f) + sizeof(stormkit::vec3f); - inline constexpr auto WINDOW_TITLE = "StormKit GameOfLife Example"; - inline constexpr auto MESH_VERTEX_BUFFER_SIZE = VERTEX_SIZE * 3; - inline constexpr auto MESH_VERTEX_BINDING_DESCRIPTIONS = std::array { - stormkit::gpu::VertexBindingDescription { .binding = 0, .stride = VERTEX_SIZE } - }; - inline constexpr auto MESH_VERTEX_ATTRIBUTE_DESCRIPTIONS = std::array { - stormkit::gpu::VertexInputAttributeDescription { .location = 0, - .binding = 0, - .format = stormkit::gpu::format::f322, - .offset = 0 }, - stormkit::gpu::VertexInputAttributeDescription { .location = 1, - .binding = 0, - .format = stormkit::gpu::format::f322, - .offset - = sizeof(stormkit::math::vec2f) } - }; - - inline constexpr auto BOARD_SIZE = 100u; - inline constexpr auto BOARD_BUFFERING_COUNT = 3u; - inline constexpr auto REFRESH_BOARD_DELTA = stormkit::Secondf { 1 }; - - inline constexpr auto SHADER_DATA = stormkit::as_bytes( -#include - ); - - IN_MODULE_LOGGER("StormKit.GameOfLife"); - -#ifdef STORMKIT_BUILD_MODULES -} -#endif +// Copyright (C) 2024 Arthur LAURENT +// This file is subject to the license terms in the LICENSE file +// found in the top-level of this distribution + +#ifdef STORMKIT_BUILD_MODULES +export module Constants; + +import std; + +import stormkit.core; +import stormkit.log; +import stormkit.gpu; + +export { +#else + #include + + #include + #include + #include +#endif + + inline constexpr auto VERTEX_SIZE + = sizeof(stormkit::math::fvec2) + sizeof(stormkit::fvec3); + inline constexpr auto WINDOW_TITLE = "StormKit GameOfLife Example"; + inline constexpr auto MESH_VERTEX_BUFFER_SIZE = VERTEX_SIZE * 3; + inline constexpr auto MESH_VERTEX_BINDING_DESCRIPTIONS = array { + stormkit::gpu::VertexBindingDescription { .binding = 0, .stride = VERTEX_SIZE } + }; + inline constexpr auto MESH_VERTEX_ATTRIBUTE_DESCRIPTIONS = array { + stormkit::gpu::VertexInputAttributeDescription { .location = 0, + .binding = 0, + .format = stormkit::gpu::format::f322, + .offset = 0 }, + stormkit::gpu::VertexInputAttributeDescription { .location = 1, + .binding = 0, + .format = stormkit::gpu::format::f322, + .offset + = sizeof(stormkit::math::fvec2) } + }; + + inline constexpr auto BOARD_SIZE = 100u; + inline constexpr auto BOARD_BUFFERING_COUNT = 3u; + inline constexpr auto REFRESH_BOARD_DELTA = stormkit::fsecond { 1 }; + + inline constexpr auto SHADER_DATA = stormkit::as_bytes( +#include + ); + + IN_MODULE_LOGGER("StormKit.GameOfLife"); + +#ifdef STORMKIT_BUILD_MODULES +} +#endif diff --git a/examples/entities/gameoflife/src/Renderer.cpp b/examples/entities/gameoflife/src/Renderer.cpp index 27b849145..cc93afd5c 100644 --- a/examples/entities/gameoflife/src/Renderer.cpp +++ b/examples/entities/gameoflife/src/Renderer.cpp @@ -28,7 +28,7 @@ auto Renderer::operator=(Renderer&&) noexcept -> Renderer& = default; auto Renderer::renderFrame() -> void { const auto& surface_extent = m_surface->extent(); - const auto surface_extentf = math::ExtentF { surface_extent }; + const auto surface_extentf = math::fextent2 { surface_extent }; if (m_surface->needRecreate()) { m_surface->recreate(); @@ -36,17 +36,17 @@ auto Renderer::renderFrame() -> void { } const auto viewports = [&] { - auto v = std::vector {}; + auto v = dyn_array {}; v.emplace_back(gpu::Viewport { - .extent = surface_extentf, - .depth = { 0, 1 } + .extent = surface_extentf, + .depth = { 0, 1 } }); return v; }(); const auto scissors = [&] { - auto s = std::vector {}; + auto s = dyn_array {}; s.emplace_back(gpu::Scissor { .extent = surface_extent }); return s; @@ -88,14 +88,12 @@ auto Renderer::updateBoard(const stormkit::image::Image& board) -> void { if (m_current_fence) m_current_fence->wait(); - const auto descriptors = into_array(gpu::Descriptor { - gpu::ImageDescriptor { .type = gpu::DescriptorType::Combined_Image_Sampler, - .binding = 0, - .layout = gpu::ImageLayout::Shader_Read_Only_Optimal, - .image_view - = makeConstObserver(m_board.image_views[m_board.current_image]), - .sampler = makeConstObserver(m_board.sampler) } - }); + const auto descriptors = into_array_of(gpu::ImageDescriptor { + .type = gpu::DescriptorType::Combined_Image_Sampler, + .binding = 0, + .layout = gpu::ImageLayout::Shader_Read_Only_Optimal, + .image_view = makeConstObserver(m_board.image_views[m_board.current_image]), + .sampler = makeConstObserver(m_board.sampler) }); m_board.descriptor_set->update(descriptors); } @@ -122,9 +120,7 @@ auto Renderer::do_initBaseRenderObjects() -> void { const auto& physical_device_info = physical_device.info(); - ilog("Using physical device {} ({:#06x})", - physical_device_info.device_name, - physical_device_info.device_id); + ilog("Using physical device {} ({:#06x})", physical_device_info.device_name, physical_device_info.device_id); m_device = physical_device.allocateLogicalDevice(); @@ -136,49 +132,46 @@ auto Renderer::do_initBaseRenderObjects() -> void { auto Renderer::do_initMeshRenderObjects() -> void { const auto& surface_extent = m_surface->extent(); - const auto surface_extentf = math::ExtentF { surface_extent }; + const auto surface_extentf = math::fextent2 { surface_extent }; m_board.vertex_shader = m_device->allocateShader(SHADER_DATA, gpu::ShaderStageFlag::Vertex); m_board.fragment_shader = m_device->allocateShader(SHADER_DATA, gpu::ShaderStageFlag::Fragment); - const auto description - = gpu::RenderPassDescription { .attachments = { { .format = m_surface->pixelFormat() } }, - .subpasses - = { { .bind_point = gpu::PipelineBindPoint::Graphics, - .attachment_refs = { { .attachment_id = 0u } } } } }; + const auto description = gpu::RenderPassDescription { + .attachments = { { .format = m_surface->pixelFormat() } }, + .subpasses = { { .bind_point = gpu::PipelineBindPoint::Graphics, .attachment_refs = { { .attachment_id = 0u } } } } + }; m_descriptor_set_layout = m_device->allocateDescriptorSetLayout(); - m_descriptor_set_layout->addBinding({ .binding = 0, - .type = gpu::DescriptorType::Combined_Image_Sampler, - .stages = gpu::ShaderStageFlag::Fragment, - .descriptor_count = 1 }); + m_descriptor_set_layout + ->addBinding({ .binding = 0, + .type = gpu::DescriptorType::Combined_Image_Sampler, + .stages = gpu::ShaderStageFlag::Fragment, + .descriptor_count = 1 }); m_descriptor_set_layout->bake(); m_descriptor_pool = m_device->allocateDescriptorPool( - std::array { - gpu::DescriptorPool::Size { gpu::DescriptorType::Combined_Image_Sampler, 1 } + array { + gpu::DescriptorPool::Size { gpu::DescriptorType::Combined_Image_Sampler, 1 } }, - 1); + 1); m_render_pass = m_device->allocateRenderPass(description); m_board.pipeline = m_device->allocateRasterPipeline(); const auto state = gpu::RasterPipelineState { .input_assembly_state = { .topology = gpu::PrimitiveTopology::Triangle_Strip }, - .viewport_state - = { .viewports = { gpu::Viewport { .position = { 0.f, 0.f }, - .extent = surface_extentf, - .depth = { 0.f, 1.f } } }, + .viewport_state = { .viewports = { gpu::Viewport { .position = { 0.f, 0.f }, + .extent = surface_extentf, + .depth = { 0.f, 1.f } } }, .scissors = { gpu::Scissor { .offset = { 0, 0 }, .extent = surface_extent } } }, - .color_blend_state - = { .attachments = { { .blend_enable = true, - .src_color_blend_factor = gpu::BlendFactor::Src_Alpha, - .dst_color_blend_factor = gpu::BlendFactor::One_Minus_Src_Alpha, - .src_alpha_blend_factor = gpu::BlendFactor::Src_Alpha, - .dst_alpha_blend_factor = gpu::BlendFactor::One_Minus_Src_Alpha, - .alpha_blend_operation = gpu::BlendOperation::Add } } }, - .dynamic_state = { { gpu::DynamicState::Viewport, gpu::DynamicState::Scissor } }, - .shader_state - = { .shaders = makeConstObserverArray(m_board.vertex_shader, m_board.fragment_shader) }, + .color_blend_state = { .attachments = { { .blend_enable = true, + .src_color_blend_factor = gpu::BlendFactor::Src_Alpha, + .dst_color_blend_factor = gpu::BlendFactor::One_Minus_Src_Alpha, + .src_alpha_blend_factor = gpu::BlendFactor::Src_Alpha, + .dst_alpha_blend_factor = gpu::BlendFactor::One_Minus_Src_Alpha, + .alpha_blend_operation = gpu::BlendOperation::Add } } }, + .dynamic_state = { { gpu::DynamicState::Viewport, gpu::DynamicState::Scissor } }, + .shader_state = { .shaders = makeConstObserverArray(m_board.vertex_shader, m_board.fragment_shader) }, /*.vertex_input_state = { .binding_descriptions = to_dyn_array(MESH_VERTEX_BINDING_DESCRIPTIONS), .input_attribute_descriptions = @@ -193,17 +186,17 @@ auto Renderer::do_initMeshRenderObjects() -> void { for (auto i : range(BOARD_BUFFERING_COUNT)) { auto& img = m_board.images.emplace_back(*m_device, gpu::Image::CreateInfo { - .extent = { BOARD_SIZE, BOARD_SIZE } + .extent = { BOARD_SIZE, BOARD_SIZE } }); m_board.image_views.emplace_back(img.createView()); } - m_board.sampler = m_device->allocateSampler( - gpu::Sampler::Settings { .mag_filter = gpu::Filter::Nearest, - .min_filter = gpu::Filter::Nearest, - .address_mode_u = gpu::SamplerAddressMode::Clamp_To_Edge, - .address_mode_v = gpu::SamplerAddressMode::Clamp_To_Edge }); + m_board.sampler = m_device->allocateSampler(gpu::Sampler::Settings { + .mag_filter = gpu::Filter::Nearest, + .min_filter = gpu::Filter::Nearest, + .address_mode_u = gpu::SamplerAddressMode::Clamp_To_Edge, + .address_mode_v = gpu::SamplerAddressMode::Clamp_To_Edge }); m_board.descriptor_set = m_descriptor_pool->allocateDescriptorSet(*m_descriptor_set_layout); @@ -212,7 +205,7 @@ auto Renderer::do_initMeshRenderObjects() -> void { auto Renderer::do_initPerFrameObjects() -> void { const auto& surface_extent = m_surface->extent(); - const auto surface_extentf = math::ExtentF { surface_extent }; + const auto surface_extentf = math::fextent2 { surface_extent }; const auto buffering_count = m_surface->bufferingCount(); m_surface_views.clear(); diff --git a/examples/entities/gameoflife/src/Renderer.mpp b/examples/entities/gameoflife/src/Renderer.cppm similarity index 83% rename from examples/entities/gameoflife/src/Renderer.mpp rename to examples/entities/gameoflife/src/Renderer.cppm index 63f6aa419..a45a6437c 100644 --- a/examples/entities/gameoflife/src/Renderer.mpp +++ b/examples/entities/gameoflife/src/Renderer.cppm @@ -1,77 +1,77 @@ - - -#ifdef STORMKIT_BUILD_MODULES -export module Renderer; - -import std; - -import stormkit.core; -import stormkit.wsi; -import stormkit.gpu; - -export { -#else - #include - - #include - #include - #include -#endif - - class Renderer { - public: - Renderer(const stormkit::wsi::Window& window); - ~Renderer(); - - Renderer(const Renderer&) = delete; - auto operator=(const Renderer&) -> Renderer& = delete; - - Renderer(Renderer&&) noexcept; - auto operator=(Renderer&&) noexcept -> Renderer&; - - auto renderFrame() -> void; - - auto updateBoard(const stormkit::image::Image& board) -> void; - - private: - auto do_initBaseRenderObjects() -> void; - auto do_initMeshRenderObjects() -> void; - auto do_initPerFrameObjects() -> void; - - const stormkit::wsi::Window* m_window = nullptr; - - std::unique_ptr m_instance; - std::unique_ptr m_device; - std::unique_ptr m_surface; - stormkit::gpu::Fence* m_current_fence = nullptr; - - const stormkit::gpu::Queue* m_queue = nullptr; - std::vector m_surface_views; - - std::unique_ptr m_descriptor_set_layout; - std::unique_ptr m_descriptor_pool; - - std::unique_ptr m_render_pass; - - struct Board { - std::vector images; - std::vector image_views; - std::unique_ptr sampler; - - stormkit::u32 current_image = 0; - - std::unique_ptr vertex_shader; - std::unique_ptr fragment_shader; - - std::unique_ptr pipeline; - - std::unique_ptr descriptor_set; - } m_board; - - std::vector m_command_buffers; - std::vector m_framebuffers; - }; - -#ifdef STORMKIT_BUILD_MODULES -} -#endif + + +#ifdef STORMKIT_BUILD_MODULES +export module Renderer; + +import std; + +import stormkit.core; +import stormkit.wsi; +import stormkit.gpu; + +export { +#else + #include + + #include + #include + #include +#endif + + class Renderer { + public: + Renderer(const stormkit::wsi::Window& window); + ~Renderer(); + + Renderer(const Renderer&) = delete; + auto operator=(const Renderer&) -> Renderer& = delete; + + Renderer(Renderer&&) noexcept; + auto operator=(Renderer&&) noexcept -> Renderer&; + + auto renderFrame() -> void; + + auto updateBoard(const stormkit::image::Image& board) -> void; + + private: + auto do_initBaseRenderObjects() -> void; + auto do_initMeshRenderObjects() -> void; + auto do_initPerFrameObjects() -> void; + + const stormkit::wsi::Window* m_window = nullptr; + + std::unique_ptr m_instance; + std::unique_ptr m_device; + std::unique_ptr m_surface; + stormkit::gpu::Fence* m_current_fence = nullptr; + + const stormkit::gpu::Queue* m_queue = nullptr; + dyn_array m_surface_views; + + std::unique_ptr m_descriptor_set_layout; + std::unique_ptr m_descriptor_pool; + + std::unique_ptr m_render_pass; + + struct Board { + dyn_array images; + dyn_array image_views; + std::unique_ptr sampler; + + stormkit::u32 current_image = 0; + + std::unique_ptr vertex_shader; + std::unique_ptr fragment_shader; + + std::unique_ptr pipeline; + + std::unique_ptr descriptor_set; + } m_board; + + dyn_array m_command_buffers; + dyn_array m_framebuffers; + }; + +#ifdef STORMKIT_BUILD_MODULES +} +#endif diff --git a/examples/entities/gameoflife/src/Systems.cpp b/examples/entities/gameoflife/src/Systems.cpp index 8f621fd99..4f0fce0d4 100644 --- a/examples/entities/gameoflife/src/Systems.cpp +++ b/examples/entities/gameoflife/src/Systems.cpp @@ -24,7 +24,7 @@ UpdateBoardSystem::~UpdateBoardSystem() UpdateBoardSystem::UpdateBoardSystem(UpdateBoardSystem&&) noexcept = default; auto UpdateBoardSystem::operator=(UpdateBoardSystem&&) noexcept -> UpdateBoardSystem& = default; -auto UpdateBoardSystem::update(stormkit::Secondf delta) -> void { +auto UpdateBoardSystem::update(stormkit::fsecond delta) -> void { const auto now = Clock::now(); if (m_is_on_edit_mode) [[unlikely]] { diff --git a/examples/entities/gameoflife/src/Systems.mpp b/examples/entities/gameoflife/src/Systems.cppm similarity index 85% rename from examples/entities/gameoflife/src/Systems.mpp rename to examples/entities/gameoflife/src/Systems.cppm index 279e12c7e..e7c4d5cf6 100644 --- a/examples/entities/gameoflife/src/Systems.mpp +++ b/examples/entities/gameoflife/src/Systems.cppm @@ -1,62 +1,62 @@ - - -#ifdef STORMKIT_BUILD_MODULES -export module Systems; - -import std; - -import stormkit.core; -import stormkit.image; -import stormkit.entities; - -import Constants; -import Renderer; - -export { -#else - #include - - #include - #include - #include - - #include "Constants.mpp" - #include "Renderer.mpp" -#endif - - class UpdateBoardSystem final: public stormkit::entities::System { - public: - UpdateBoardSystem(stormkit::image::Image& board, - Renderer& renderer, - stormkit::entities::EntityManager& manager); - ~UpdateBoardSystem() override; - - UpdateBoardSystem(UpdateBoardSystem&&) noexcept; - auto operator=(UpdateBoardSystem&&) noexcept -> UpdateBoardSystem&; - - auto update(stormkit::Secondf delta) -> void override; - auto post_update() -> void override; - - auto setEditModeEnabled(bool enabled) noexcept { m_is_on_edit_mode = enabled; } - - auto incrementDelta(stormkit::Secondf delta) { m_refresh_board_delta += delta; } - - private: - using Clock = std::chrono::high_resolution_clock; - - auto on_message_received(const stormkit::entities::Message& message) -> void override {}; - - bool m_is_on_edit_mode = true; - stormkit::image::Image* m_board = nullptr; - Renderer* m_renderer = nullptr; - - Clock::time_point m_last_update; - - bool m_updated = true; - - stormkit::Secondf m_refresh_board_delta = REFRESH_BOARD_DELTA; - }; - -#ifdef STORMKIT_BUILD_MODULES -} -#endif + + +#ifdef STORMKIT_BUILD_MODULES +export module Systems; + +import std; + +import stormkit.core; +import stormkit.image; +import stormkit.entities; + +import Constants; +import Renderer; + +export { +#else + #include + + #include + #include + #include + + #include "Constants.mpp" + #include "Renderer.mpp" +#endif + + class UpdateBoardSystem final: public stormkit::entities::System { + public: + UpdateBoardSystem(stormkit::image::Image& board, + Renderer& renderer, + stormkit::entities::EntityManager& manager); + ~UpdateBoardSystem() override; + + UpdateBoardSystem(UpdateBoardSystem&&) noexcept; + auto operator=(UpdateBoardSystem&&) noexcept -> UpdateBoardSystem&; + + auto update(stormkit::fsecond delta) -> void override; + auto post_update() -> void override; + + auto setEditModeEnabled(bool enabled) noexcept { m_is_on_edit_mode = enabled; } + + auto incrementDelta(stormkit::fsecond delta) { m_refresh_board_delta += delta; } + + private: + using Clock = std::chrono::high_resolution_clock; + + auto on_message_received(const stormkit::entities::Message& message) -> void override {}; + + bool m_is_on_edit_mode = true; + stormkit::image::Image* m_board = nullptr; + Renderer* m_renderer = nullptr; + + Clock::time_point m_last_update; + + bool m_updated = true; + + stormkit::fsecond m_refresh_board_delta = REFRESH_BOARD_DELTA; + }; + +#ifdef STORMKIT_BUILD_MODULES +} +#endif diff --git a/examples/entities/gameoflife/src/main.cpp b/examples/entities/gameoflife/src/main.cpp index 13eee9992..4be3cea29 100644 --- a/examples/entities/gameoflife/src/main.cpp +++ b/examples/entities/gameoflife/src/main.cpp @@ -14,7 +14,7 @@ import App; #include "App.mpp" #endif -auto main([[maybe_unused]] std::span args) -> int { +auto main([[maybe_unused]] array_view args) -> int { using namespace stormkit; setup_signal_handler(); diff --git a/examples/entities/gameoflife/xmake.lua b/examples/entities/gameoflife/xmake.lua index 9e96e6baf..221d097be 100644 --- a/examples/entities/gameoflife/xmake.lua +++ b/examples/entities/gameoflife/xmake.lua @@ -1,29 +1,29 @@ -if has_config("enable_gpu") and has_config("enable_wsi") and has_config("enable-image") then - target("game_of_life") - do - set_kind("binary") - set_languages("cxxlatest", "clatest") +-- if has_config("enable_gpu") and has_config("enable_wsi") and has_config("enable-image") then +-- target("game_of_life") +-- do +-- set_kind("binary") +-- set_languages("cxxlatest", "clatest") - add_packages("nzsl") - add_deps("core", "main", "log", "wsi", "gpu", "image") +-- add_packages("nzsl") +-- add_deps("core", "main", "log", "wsi", "gpu", "image") - add_rules("stormkit.utils.nzsl2spv") +-- add_rules("stormkit.utils.nzsl2spv") - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end +-- if is_mode("debug") then +-- add_defines("STORMKIT_BUILD_DEBUG") +-- add_defines("STORMKIT_ASSERT=1") +-- set_suffixname("-d") +-- else +-- add_defines("STORMKIT_ASSERT=0") +-- end - add_files("src/*.cpp") - add_files("src/*.mpp") - add_files("shaders/*.nzsl") - if is_plat("windows") then add_files("win32/*.manifest") end +-- add_files("src/*.cpp") +-- add_files("src/*.cppm") +-- add_files("shaders/*.nzsl") +-- if is_plat("windows") then add_files("win32/*.manifest") end - add_rules("platform.windows.subsystem.windows") +-- add_rules("platform.windows.subsystem.windows") - set_group("examples/stormkit-entities") - end -end +-- set_group("examples/stormkit-entities") +-- end +-- end diff --git a/src/wsi/linux/mouse.mpp b/examples/entities/xmake.lua similarity index 100% rename from src/wsi/linux/mouse.mpp rename to examples/entities/xmake.lua diff --git a/examples/gpu/common/app.cppm b/examples/gpu/common/app.cppm new file mode 100644 index 000000000..9fe4d8c3c --- /dev/null +++ b/examples/gpu/common/app.cppm @@ -0,0 +1,150 @@ +// Copyright (C) 2024 Arthur LAURENT +// This file is subject to the license terms in the LICENSE file +// found in the top-level of this distribution + +module; + +#include +#include + +#include + +#include + +export module gpu_app; + +import std; +import stormkit; + +export import :logger; + +using namespace stormkit; + +namespace stdr = std::ranges; +namespace stdv = std::views; + +NAMED_LOGGER(vulkan_logger, "vulkan") + +extern "C" auto debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT, + const VkDebugUtilsMessengerCallbackDataEXT* callback_data, + void*) noexcept -> u32 { + EXPECTS(callback_data); + auto message = std::format("{}", callback_data->pMessage); + + if (check_flag_bit(severity, VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)) vulkan_logger.ilog("{}", message); + else if (check_flag_bit(severity, VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)) + vulkan_logger.dlog("{}", message); + else if (check_flag_bit(severity, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)) + vulkan_logger.elog("{}", message); + else if (check_flag_bit(severity, VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)) + vulkan_logger.wlog("{}", message); + + return 0; +} + +static constexpr auto ENABLE_VALIDATION_LAYERS = false; + +export namespace base { + class Application { + public: + auto run(this auto& self, array_view args) { + wsi::parse_args(args); + log::parse_args(args); + + const auto example_name = self.example_name(); + + auto logger_singleton = log::Logger::create_logger_instance(); + + self.init_window(example_name); + self.init_gpu(example_name); + + self.init_example(); + + self.m_window->event_loop([&self] noexcept { self.run_example(); }); + + TryDiscardAssert(self.m_raster_queue->wait_idle(), "Failed to wait for raster queue"); + self.m_device->wait_idle(); + + if constexpr (requires { self.deinit(); }) self.deinit(); + } + + protected: + DeferInit m_window; + DeferInit m_instance; + DeferInit m_debug_callback; + DeferInit m_surface; + DeferInit m_physical_device; + DeferInit m_device; + DeferInit m_swapchain; + DeferInit m_raster_queue; + DeferInit m_command_pool; + + private: + auto init_window(string_view example_name) noexcept -> void { + m_window = wsi::Window::open(std::format("Stormkit GPU {} example", example_name), + { 800_u32, 600_u32 }, + wsi::WindowFlag::DEFAULT | wsi::WindowFlag::EXTERNAL_CONTEXT); + m_window->on([this](u8 /*id*/, wsi::Key key, char /*c*/) mutable noexcept { + if (key == wsi::Key::ESCAPE) m_window->close(); + }); + } + + auto init_gpu(string_view example_name) noexcept -> void { + // initialize gpu backend (vulkan or webgpu depending the platform) + TryDiscardAssert(gpu::initialize_backend(), "Failed to initialize gpu backend"); + + // create gpu instance and attach surface to window + m_instance = TryAssert(gpu::Instance::create({ .application_name = string { example_name }, + .enable_validation_layers = ENABLE_VALIDATION_LAYERS }), + "Failed to initialize gpu instance"); + + if (ENABLE_VALIDATION_LAYERS) { + m_debug_callback = TryAssert(gpu::DebugCallback::create(m_instance, { .messenger_closure = debug_callback }), + "Failed to initialize gpu instance"); + } + + m_surface = TryAssert(gpu::Surface::create_from_window(m_instance, m_window), + "Failed to initialize window gpu surface"); + + // pick the best physical device + const auto& physical_devices = m_instance->physical_devices(); + if (stdr::empty(physical_devices)) { + elog("No render physical device found!"); + return; + } + ilog("Physical devices: {}", physical_devices); + + m_physical_device = physical_devices.front(); + auto score = gpu::score_physical_device(*m_physical_device); + for (auto i : range(1_u32, stdr::size(physical_devices))) { + const auto& d = physical_devices[i]; + const auto d_score = gpu::score_physical_device(d); + if (d_score > score) { + m_physical_device = d; + score = d_score; + } + } + + ilog("Picked gpu: {}", *m_physical_device); + + // create gpu device + m_device = TryAssert(gpu::Device::create(m_physical_device, {}), "Failed to initialize gpu device"); + + // create swapchain + const auto window_extent = m_window->extent(); + m_swapchain = TryAssert(gpu::SwapChain::create(m_device, { gpu::as_view(m_surface), window_extent }), + "Failed to create swapchain"); + + const auto queue_entries = m_device->queue_entries(); + const auto it = stdr::find_if(queue_entries, gpu::monadic::find_queue()); + ensures(it != stdr::cend(queue_entries), "No raster queue found!"); + + m_raster_queue = gpu::Queue::create(m_device, *it); + + m_command_pool = TryAssert(gpu::CommandPool::create(m_device, { .queue = m_raster_queue }), + "Failed to create command pool " + "command pool"); + } + }; +} // namespace base diff --git a/examples/gpu/common/app.mpp b/examples/gpu/common/app.mpp deleted file mode 100644 index caec4ac2a..000000000 --- a/examples/gpu/common/app.mpp +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (C) 2024 Arthur LAURENT -// This file is subject to the license terms in the LICENSE file -// found in the top-level of this distribution - -export module gpu_app; - -import std; -import stormkit; - -using namespace stormkit; - -namespace stdr = std::ranges; -namespace stdv = std::views; - -export namespace base { - class Application { - public: - auto run(this auto& self, std::span args) { - const auto example_name = self.example_name(); - - auto logger_singleton = log::Logger::create_logger_instance(); - - wsi::parse_args(args); - - self.init_window(example_name); - self.init_gpu(example_name); - - self.init_example(); - - self.m_window->event_loop([&self] noexcept { self.run_example(); }); - - self.m_raster_queue->wait_idle(); - self.m_device->wait_idle(); - - if constexpr (requires { self.deinit(); }) self.deinit(); - } - - protected: - DeferInit m_window; - DeferInit m_instance; - DeferInit m_surface; - OptionalRef m_physical_device; - DeferInit m_device; - DeferInit m_swapchain; - DeferInit m_raster_queue; - DeferInit m_command_pool; - - private: - auto init_window(std::string_view example_name) noexcept -> void { - m_window = wsi::Window::open(std::format("Stormkit GPU {} example", example_name), - { 800_u32, 600_u32 }, - wsi::WindowFlag::DEFAULT - | wsi::WindowFlag::EXTERNAL_CONTEXT); - m_window->on([this](u8 /*id*/, - wsi::Key key, - char /*c*/) mutable noexcept { - if (key == wsi::Key::ESCAPE) m_window->close(); - }); - } - - auto init_gpu(std::string_view example_name) noexcept -> void { - // initialize gpu backend (vulkan or webgpu depending the platform) - *gpu::initialize_backend() - .transform_error(monadic::assert("Failed to initialize gpu backend")); - - // create gpu instance and attach surface to window - m_instance = gpu::Instance::create(std::string { example_name }) - .transform_error(monadic::assert("Failed to initialize gpu instance")) - .value(); - - m_surface = gpu::Surface::create_from_window(m_instance, m_window) - .transform_error(monadic:: - assert("Failed to initialize window gpu surface")) - .value(); - // pick the best physical device - const auto& physical_devices = m_instance->physical_devices(); - if (stdr::empty(physical_devices)) { - log::Logger::elog("No render physical device found!"); - return; - } - log::Logger::ilog("Physical devices: {}", physical_devices); - - m_physical_device = as_opt_ref(physical_devices.front()); - auto score = gpu::score_physical_device(*m_physical_device); - for (auto i = 1u; i < stdr::size(physical_devices); ++i) { - const auto& d = physical_devices[i]; - const auto d_score = gpu::score_physical_device(d); - if (d_score > score) { - m_physical_device = as_opt_ref(d); - score = d_score; - } - } - - log::Logger::ilog("Picked gpu: {}", *m_physical_device); - - // create gpu device - m_device = gpu::Device::create(*m_physical_device, m_instance) - .transform_error(monadic::assert("Failed to initialize gpu device")) - .value(); - - // create swapchain - const auto window_extent = m_window->extent(); - m_swapchain = gpu::SwapChain::create(m_device, m_surface, window_extent) - .transform_error(monadic::assert("Failed to create swapchain")) - .value(); - - m_raster_queue = gpu::Queue::create(m_device, m_device->raster_queue_entry()); - - m_command_pool = gpu::CommandPool::create(m_device) - .transform_error(monadic::assert("Failed to create raster queue " - "command pool")) - .value(); - } - }; -} // namespace base diff --git a/examples/gpu/imgui/iOS/Info.plist b/examples/gpu/imgui/iOS/Info.plist deleted file mode 100644 index a62a3592d..000000000 --- a/examples/gpu/imgui/iOS/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - diff --git a/examples/gpu/imgui/iOS/LaunchScreen.storyboard b/examples/gpu/imgui/iOS/LaunchScreen.storyboard deleted file mode 100644 index 436820912..000000000 --- a/examples/gpu/imgui/iOS/LaunchScreen.storyboard +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/gpu/imgui/macOS/Info.plist b/examples/gpu/imgui/macOS/Info.plist deleted file mode 100644 index f68f66070..000000000 --- a/examples/gpu/imgui/macOS/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/gpu/imgui/src/logger.cppm b/examples/gpu/imgui/src/logger.cppm new file mode 100644 index 000000000..ca004b433 --- /dev/null +++ b/examples/gpu/imgui/src/logger.cppm @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Arthur LAURENT +// This file is subject to the license terms in the LICENSE file +// found in the top-level of this distribution + +module; + +#include + +export module gpu_app:logger; + +import std; + +import stormkit.log; + +export { IN_MODULE_LOGGER("Imgui"); } diff --git a/examples/gpu/imgui/src/main.cpp b/examples/gpu/imgui/src/main.cpp index ba751c442..94dd9fc94 100644 --- a/examples/gpu/imgui/src/main.cpp +++ b/examples/gpu/imgui/src/main.cpp @@ -7,6 +7,7 @@ import std; import stormkit; import gpu_app; +#include #include #include #include @@ -16,8 +17,6 @@ import gpu_app; #include #undef assert -LOGGER("stormkit.examples.gpu.imgui"); - namespace stdr = std::ranges; namespace stdfs = std::filesystem; @@ -31,13 +30,17 @@ struct SubmissionResource { }; struct SwapchainImageResource { - Ref image; - gpu::ImageView view; - gpu::FrameBuffer framebuffer; - gpu::Semaphore render_finished; + gpu::view::Image image; + gpu::ImageView view; + gpu::Semaphore render_finished; }; -static constexpr auto BUFFERING_COUNT = 2; +namespace { + constexpr auto BUFFERING_COUNT = 2_u32; + constexpr auto POOL_SIZES = array { + gpu::DescriptorPool::Size { .type = gpu::DescriptorType::COMBINED_IMAGE_SAMPLER, .descriptor_count = BUFFERING_COUNT } + }; +} // namespace class Application: public base::Application { public: @@ -48,44 +51,23 @@ class Application: public base::Application { auto init_resources() -> void { // initialilze descriptor pool - static constexpr auto POOL_SIZES = std::array { - gpu::DescriptorPool::Size { .type = gpu::DescriptorType::COMBINED_IMAGE_SAMPLER, - .descriptor_count = BUFFERING_COUNT } - }; - m_descriptor_pool = gpu::DescriptorPool::create(m_device, POOL_SIZES, BUFFERING_COUNT) - .transform_error(monadic::assert("Failed to create descriptor pool")) - .value(); - - // initialize render pass - m_render_pass - = gpu::RenderPass:: - create(m_device, - { .attachments = { { .format = m_swapchain->pixel_format() } }, - .subpasses = { { .bind_point = gpu::PipelineBindPoint::GRAPHICS, - .color_attachment_refs = { { .attachment_id = 0u } } } } }) - .transform_error(monadic::assert("Failed to create render pass")) - .value(); - - const auto window_extent = m_window->extent(); + m_descriptor_pool = TryAssert(gpu::DescriptorPool::create(m_device, POOL_SIZES, BUFFERING_COUNT), + "Failed to create descriptor pool!"); // create present engine resources - m_submission_resources = init_by>([&](auto& out) noexcept { + m_submission_resources = init_by>([&](auto& out) noexcept { out.reserve(BUFFERING_COUNT); for (auto _ : range(BUFFERING_COUNT)) { out.push_back({ - .in_flight = gpu::Fence::create_signaled(m_device) - .transform_error(monadic:: - assert("Failed to create swapchain image " - "in flight fence")) - .value(), - .image_available = gpu::Semaphore::create(m_device) - .transform_error(monadic::assert("Failed to create " - "present wait semaphore")) - .value(), - .render_cmb = m_command_pool->create_command_buffer() - .transform_error(monadic::assert("Failed to create transition " - "command buffers")) - .value(), + .in_flight = TryAssert(gpu::Fence::create_signaled(m_device), + "Failed to create swapchain image " + "in flight fence!"), + .image_available = TryAssert(gpu::Semaphore::create(m_device), + "Failed to create " + "present wait semaphore!"), + .render_cmb = TryAssert(m_command_pool->create_command_buffer(), + "Failed to create transition " + "command buffers!"), }); } }); @@ -93,64 +75,43 @@ class Application: public base::Application { // transition swapchain image to present image const auto& images = m_swapchain->images(); - const auto image_count = stdr::size(images); - auto transition_cmbs - = m_command_pool->create_command_buffers(image_count) - .transform_error(monadic::assert("Failed to create transition command buffers")) - .value(); + const auto image_count = stdr::size(images); + auto transition_cmbs = TryAssert(m_command_pool->create_command_buffers(image_count), + "Failed to create transition command buffers!"); m_image_resources.reserve(stdr::size(images)); auto image_index = 0u; for (const auto& swap_image : images) { - auto view = gpu::ImageView::create(m_device, swap_image) - .transform_error(core::monadic:: - assert("Failed to create swapchain image view")) - .value(); - auto framebuffer = m_render_pass - ->create_frame_buffer(m_device, window_extent, to_refs(view)) - .transform_error(core::monadic::assert( - std::format("Failed to create framebuffer for image {}", - image_index))) - .value(); - - m_image_resources.push_back({ - .image = as_ref(swap_image), - .view = std::move(view), - .framebuffer = std::move(framebuffer), - .render_finished = gpu::Semaphore::create(m_device) - .transform_error(core::monadic::assert("Failed to create render " - "signal semaphore")) - .value(), - }); + auto view = TryAssert(gpu::ImageView::create(m_device, { swap_image }), "Failed to create swapchain image view!"); + + m_image_resources.push_back({ .image = swap_image, + .view = std::move(view), + .render_finished = TryAssert(gpu::Semaphore::create(m_device), + "Failed to create render " + "signal semaphore!") }); auto& transition_cmb = transition_cmbs[image_index]; - *transition_cmb.begin(true) - .transform_error(monadic:: - assert("Failed to begin texture transition command buffer")) - .value() - ->begin_debug_region(std::format("transition image {}", image_index)) - .transition_image_layout(swap_image, - gpu::ImageLayout::UNDEFINED, - gpu::ImageLayout::PRESENT_SRC) - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to begin texture transition command " - "buffer")); + TryDiscardAssert((transition_cmb.record([&](auto cmb) noexcept { + cmb.begin_debug_region(std::format("Transition image {}", image_index)) + .transition_image_layout(swap_image, + gpu::ImageLayout::UNDEFINED, + gpu::ImageLayout::PRESENT_SRC) + .end_debug_region(); + })), + std::format("Failed to record transition cmb {}!", image_index)); ++image_index; } - const auto fence = gpu::Fence::create(m_device) - .transform_error(monadic::assert("Failed to create transition fence")) - .value(); - const auto cmbs = to_refs(transition_cmbs); + const auto fence = TryAssert(gpu::Fence::create(m_device), "Failed to create transition fence!"); - m_raster_queue->submit({ .command_buffers = cmbs }, as_ref(fence)) - .transform_error(monadic::assert("Failed to submit texture transition command buffers")) - .value(); + const auto cmbs = to_views(transition_cmbs); + + TryAssert(m_raster_queue->submit({ .command_buffers = cmbs }, fence), + "Failed to submit texture transition command buffers!"); // wait for transition to be done - fence.wait().transform_error(monadic::assert()); + TryAssert(fence.wait(), ""); } auto init_imgui() -> void { @@ -160,70 +121,81 @@ class Application: public base::Application { io.DisplaySize.x = m_window->extent().to().width; io.DisplaySize.y = m_window->extent().to().height; + const auto format = gpu::vk::to_vk(m_swapchain->pixel_format()); /*const*/ auto init_info = ImGui_ImplVulkan_InitInfo { - .ApiVersion = VK_API_VERSION_1_1, + .ApiVersion = VK_API_VERSION_1_3, .Instance = m_instance->native_handle(), .PhysicalDevice = m_physical_device->native_handle(), .Device = m_device->native_handle(), .QueueFamily = 0, .Queue = m_raster_queue->native_handle(), .DescriptorPool = m_descriptor_pool->native_handle(), - .RenderPass = m_render_pass->native_handle(), + .DescriptorPoolSize = 0, .MinImageCount = BUFFERING_COUNT, .ImageCount = BUFFERING_COUNT, - .MSAASamples = VK_SAMPLE_COUNT_1_BIT, .PipelineCache = nullptr, - .Subpass = 0, - .DescriptorPoolSize = 0, - .UseDynamicRendering = false, - .PipelineRenderingCreateInfo = {}, + .PipelineInfoMain = { + .RenderPass = nullptr, + .Subpass = {}, + .MSAASamples = {}, + .PipelineRenderingCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .pNext = nullptr, + .viewMask = 0, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &format, + .depthAttachmentFormat = {}, + .stencilAttachmentFormat = {} + + }, + }, + .UseDynamicRendering = true, .Allocator = nullptr, .CheckVkResultFn = [](auto result) static noexcept { - if (result != VK_SUCCESS) elog("{}", gpu::from_vk(result)); + if (result != VK_SUCCESS) elog("{}", gpu::vk::from_vk(result)); }, .MinAllocationSize = 1024 * 1024, + .CustomShaderVertCreateInfo = {}, + .CustomShaderFragCreateInfo = {}, }; - ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_1, gpu::imgui_vk_loader, &*m_device); + ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_1, gpu::vk::imgui_vk_loader, &*m_device); ImGui_ImplVulkan_Init(&init_info); - m_window - ->on(wsi::KeyDownEventFunc { [this, - &io](u8 /*id*/, wsi::Key key, char c) mutable noexcept { - if (key == wsi::Key::ESCAPE) m_window->close(); - io.AddInputCharactersUTF8(&c); - } }, - wsi::MouseMovedEventFunc { - [&io](u8 /*id*/, const math::vec2i& position) mutable noexcept { - const auto _position = position.to(); - - io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); - io.AddMousePosEvent(_position.x, _position.y); - } }, - wsi::MouseButtonDownEventFunc { - [&io](u8 /*id*/, wsi::MouseButton button, const math::vec2i&) mutable noexcept { - auto mouse_button = -1; - if (button == wsi::MouseButton::LEFT) mouse_button = 0; - if (button == wsi::MouseButton::RIGHT) mouse_button = 1; - if (button == wsi::MouseButton::MIDDLE) mouse_button = 2; - if (button == wsi::MouseButton::BUTTON_1) mouse_button = 3; - if (button == wsi::MouseButton::BUTTON_2) mouse_button = 4; - if (mouse_button == -1) return; - io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); - io.AddMouseButtonEvent(mouse_button, true); - } }, - wsi::MouseButtonUpEventFunc { - [&io](u8 /*id*/, wsi::MouseButton button, const math::vec2i&) mutable noexcept { - auto mouse_button = -1; - if (button == wsi::MouseButton::LEFT) mouse_button = 0; - if (button == wsi::MouseButton::RIGHT) mouse_button = 1; - if (button == wsi::MouseButton::MIDDLE) mouse_button = 2; - if (button == wsi::MouseButton::BUTTON_1) mouse_button = 3; - if (button == wsi::MouseButton::BUTTON_2) mouse_button = 4; - if (mouse_button == -1) return; - io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); - io.AddMouseButtonEvent(mouse_button, false); - } }); + m_window->on(wsi::KeyDownEventFunc { [this, &io](u8 /*id*/, wsi::Key key, char c) mutable noexcept { + if (key == wsi::Key::ESCAPE) m_window->close(); + io.AddInputCharactersUTF8(&c); + } }, + wsi::MouseMovedEventFunc { [&io](u8 /*id*/, const math::ivec2& position) mutable noexcept { + const auto _position = position.to(); + + io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); + io.AddMousePosEvent(_position.x, _position.y); + } }, + wsi::MouseButtonDownEventFunc { + [&io](u8 /*id*/, wsi::MouseButton button, const math::ivec2&) mutable noexcept { + auto mouse_button = -1; + if (button == wsi::MouseButton::LEFT) mouse_button = 0; + if (button == wsi::MouseButton::RIGHT) mouse_button = 1; + if (button == wsi::MouseButton::MIDDLE) mouse_button = 2; + if (button == wsi::MouseButton::BUTTON_1) mouse_button = 3; + if (button == wsi::MouseButton::BUTTON_2) mouse_button = 4; + if (mouse_button == -1) return; + io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent(mouse_button, true); + } }, + wsi::MouseButtonUpEventFunc { + [&io](u8 /*id*/, wsi::MouseButton button, const math::ivec2&) mutable noexcept { + auto mouse_button = -1; + if (button == wsi::MouseButton::LEFT) mouse_button = 0; + if (button == wsi::MouseButton::RIGHT) mouse_button = 1; + if (button == wsi::MouseButton::MIDDLE) mouse_button = 2; + if (button == wsi::MouseButton::BUTTON_1) mouse_button = 3; + if (button == wsi::MouseButton::BUTTON_2) mouse_button = 4; + if (mouse_button == -1) return; + io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); + io.AddMouseButtonEvent(mouse_button, false); + } }); } auto run_example() { @@ -240,64 +212,55 @@ class Application: public base::Application { const auto& wait = submission_resource.image_available; auto& in_flight = submission_resource.in_flight; - const auto acquire_next_image = bind_front(&gpu::SwapChain::acquire_next_image, - &*m_swapchain, - 100ms, - std::cref(wait)); - const auto extract_index = [](auto&& _result) static noexcept { - auto&& [result, _image_index] = _result; - return _image_index; - }; + TryAssert(in_flight.wait(), "Failed to wait in_flight fence!"); + TryAssert(in_flight.reset(), "Failed to reset in_flight fence!"); - const auto image_index - = in_flight.wait() - .transform([&in_flight](auto&&) mutable noexcept { in_flight.reset(); }) - .and_then(acquire_next_image) - .transform(extract_index) - .transform_error(monadic::assert("Failed to acquire next swapchain image")) - .value(); + const auto&& [_, image_index] = TryAssert(m_swapchain->acquire_next_image(100ms, wait), + "Failed to acquire next swapchain image!"); const auto& swapchain_image_resource = m_image_resources[image_index]; - const auto& framebuffer = swapchain_image_resource.framebuffer; const auto& signal = swapchain_image_resource.render_finished; - static constexpr auto PIPELINE_FLAGS = std::array { - gpu::PipelineStageFlag::COLOR_ATTACHMENT_OUTPUT + static constexpr auto PIPELINE_FLAGS = array { gpu::PipelineStageFlag::COLOR_ATTACHMENT_OUTPUT }; + + const auto window_extent = m_window->extent().to(); + const auto rendering_info = gpu::RenderingInfo { + .render_area = { .x = 0, .y = 0, .width = window_extent.width, .height = window_extent.height }, + .color_attachments = { { .image_view = swapchain_image_resource.view, + .layout = gpu::ImageLayout::ATTACHMENT_OPTIMAL, + .clear_value = gpu::ClearColor { .color = colors::SILVER } } } }; // render in it auto& render_cmb = submission_resource.render_cmb; - render_cmb.reset() - .transform_error(monadic::assert("Failed to reset render command buffer")) - .value() - ->begin() - .transform_error(monadic::assert("Failed to begin render command buffer")) - .value() - ->begin_debug_region("Render imgui") - .begin_render_pass(m_render_pass, framebuffer); - - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), render_cmb.native_handle()); - - *render_cmb.end_render_pass() - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to end render command buffer")) - .value() - ->submit(m_raster_queue, - as_refs(wait), - PIPELINE_FLAGS, - as_refs(signal), - as_ref(in_flight)) - .transform_error(monadic::assert("Failed to submit render command buffer")); + TryAssert(render_cmb.reset(), std::format("Failed to reset render cmb {}!", image_index)); + TryDiscardAssert((render_cmb.record([&](auto cmb) noexcept { + cmb + .transition_image_layout(swapchain_image_resource.image, + gpu::ImageLayout::PRESENT_SRC, + gpu::ImageLayout::ATTACHMENT_OPTIMAL) + .begin_debug_region("Render imgui") + .begin_rendering(rendering_info); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmb); + + cmb + .end_rendering() // + .end_debug_region() + .transition_image_layout(swapchain_image_resource.image, + gpu::ImageLayout::ATTACHMENT_OPTIMAL, + gpu::ImageLayout::PRESENT_SRC); + })), + std::format("Failed to record render cmb {}!", image_index)); + + TryDiscardAssert(render_cmb.submit(m_raster_queue, gpu::as_views(wait), PIPELINE_FLAGS, gpu::as_views(signal), in_flight), + "Failed to submit render command buffer"); // present it - auto update_current_frame = [this](auto&&) mutable noexcept { - if (++m_current_frame >= BUFFERING_COUNT) m_current_frame = 0; - }; + TryDiscardAssert(m_raster_queue->present(gpu::as_views(m_swapchain), gpu::as_views(signal), as_view(image_index)), + "Failed to present swapchain image"); - m_raster_queue->present(as_refs(m_swapchain), as_refs(signal), as_view(image_index)) - .transform(update_current_frame) - .transform_error(monadic::assert("Failed to present swapchain image")); + if (++m_current_frame >= BUFFERING_COUNT) m_current_frame = 0; } auto deinit() { @@ -305,17 +268,16 @@ class Application: public base::Application { ImGui::DestroyContext(); } - constexpr auto example_name() const noexcept -> std::string_view { return "Imgui"; } + constexpr auto example_name() const noexcept -> string_view { return "Imgui"; } private: - DeferInit m_render_pass; - DeferInit m_descriptor_pool; - std::vector m_submission_resources; - std::vector m_image_resources; - usize m_current_frame = 0_usize; + DeferInit m_descriptor_pool; + dynarray m_submission_resources; + dynarray m_image_resources; + usize m_current_frame = 0_usize; }; -auto main(std::span args) -> int { +auto main(array_view args) -> int { auto app = Application {}; app.run(args); return 0; diff --git a/examples/gpu/imgui/win32/manifest.manifest b/examples/gpu/imgui/win32/manifest.manifest deleted file mode 100644 index 4a9b5f6a6..000000000 --- a/examples/gpu/imgui/win32/manifest.manifest +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - PerMonitorV2 - - - diff --git a/examples/gpu/imgui/xmake.lua b/examples/gpu/imgui/xmake.lua index b7cfa0a5b..7eca49909 100644 --- a/examples/gpu/imgui/xmake.lua +++ b/examples/gpu/imgui/xmake.lua @@ -3,31 +3,21 @@ add_requires("imgui", { system = false, debug = true, }) -target("imgui", function() - set_kind("binary") - set_languages("cxxlatest", "clatest") - - add_rules("stormkit.flags") - -- add_rules("platform.windows.subsystem.windows") - add_rules("platform.windows.subsystem.console") - add_deps("core", "main", "log", "wsi", "gpu", "stormkit") +target("imgui", function() + add_rules("stormkit::example") - add_packages("imgui") + add_files("src/*.cpp", "src/*.cppm", "../common/app.cppm") - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") + if get_config("devmode") then + add_defines(format('RESOURCE_DIR="%s"', path.unix(path.join(os.projectdir(), "gpu/imgui")))) + add_defines(format('SHADER_DIR="%s"', path.unix("$(builddir)/shader"))) end - - add_files("../common/app.mpp") - add_files("src/*.cpp") - if is_plat("windows") then add_files("win32/*.manifest") end + add_embeddirs("$(builddir)/shaders") if get_config("devmode") then set_rundir("$(projectdir)") end + add_packages("imgui", "volk", "vulkan-headers") + set_group("examples/stormkit-gpu") end) diff --git a/examples/gpu/textured_cube/iOS/Info.plist b/examples/gpu/textured_cube/iOS/Info.plist deleted file mode 100644 index a62a3592d..000000000 --- a/examples/gpu/textured_cube/iOS/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - diff --git a/examples/gpu/textured_cube/iOS/LaunchScreen.storyboard b/examples/gpu/textured_cube/iOS/LaunchScreen.storyboard deleted file mode 100644 index 436820912..000000000 --- a/examples/gpu/textured_cube/iOS/LaunchScreen.storyboard +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/gpu/textured_cube/macOS/Info.plist b/examples/gpu/textured_cube/macOS/Info.plist deleted file mode 100644 index f68f66070..000000000 --- a/examples/gpu/textured_cube/macOS/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/gpu/textured_cube/shaders/textured_cube.nzsl b/examples/gpu/textured_cube/shaders/textured_cube.nzsl index 8580764db..0c3dd2ac1 100644 --- a/examples/gpu/textured_cube/shaders/textured_cube.nzsl +++ b/examples/gpu/textured_cube/shaders/textured_cube.nzsl @@ -29,7 +29,7 @@ external { } [entry(vert)] -fn main(input: VertIn) -> VertOut { +fn vert_main(input: VertIn) -> VertOut { let output: VertOut; output.position = viewer.proj * viewer.view * viewer.model * vec4[f32](input.position, 1.); @@ -49,7 +49,7 @@ external { } [entry(frag)] -fn main(input: VertOut) -> FragOut { +fn frag_main(input: VertOut) -> FragOut { let output: FragOut; output.color = texture.Sample(input.uv); diff --git a/examples/gpu/textured_cube/src/logger.cppm b/examples/gpu/textured_cube/src/logger.cppm new file mode 100644 index 000000000..30e39ca8f --- /dev/null +++ b/examples/gpu/textured_cube/src/logger.cppm @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Arthur LAURENT +// This file is subject to the license terms in the LICENSE file +// found in the top-level of this distribution + +module; + +#include + +export module gpu_app:logger; + +import std; + +import stormkit.log; + +export { IN_MODULE_LOGGER("Textured Cube"); } diff --git a/examples/gpu/textured_cube/src/main.cpp b/examples/gpu/textured_cube/src/main.cpp index 7b2566504..a658dc079 100644 --- a/examples/gpu/textured_cube/src/main.cpp +++ b/examples/gpu/textured_cube/src/main.cpp @@ -10,21 +10,21 @@ import stormkit; import gpu_app; #include +#include #include #include -LOGGER("stormkit.examples.gpu.textured_cube"); - #ifndef SHADER_DIR - #define SHADER_DIR "../share/shaders" +static constexpr auto SHADER_DIR "../share/stormkit/shaders/" #endif -#ifndef TEXTURE_DIR - #define TEXTURE_DIR "../share/textures" +#ifndef RESOURCE_DIR + static constexpr auto RESOURCE_DIR "../share/stormkit/" #endif -namespace stdc = std::chrono; -namespace stdr = std::ranges; + namespace stdc = std::chrono; +namespace stdr = std::ranges; +namespace stdfs = std::filesystem; using clock = stdc::high_resolution_clock; @@ -41,151 +41,145 @@ struct SubmissionResource { }; struct SwapchainImageResource { - Ref image; - gpu::ImageView view; - gpu::Image depth_image; - gpu::ImageView depth_view; - gpu::FrameBuffer framebuffer; - gpu::Semaphore render_finished; + gpu::view::Image image; + gpu::ImageView view; + gpu::Image depth_image; + gpu::ImageView depth_view; + gpu::Semaphore render_finished; }; struct Vertex { - math::vec3f position; - math::vec2f uv; - - static constexpr auto attribute_descriptions() noexcept - -> std::array { - return { - gpu::VertexInputAttributeDescription { - 0, 0, - gpu::PixelFormat::RGB32F, - offsetof(Vertex, position) }, - gpu::VertexInputAttributeDescription { - 1, 0, - gpu::PixelFormat::RG32F, - offsetof(Vertex, uv) }, - }; + math::fvec3 position; + math::fvec2 uv; + + static constexpr auto attribute_descriptions() noexcept -> array { + return to_array({ + { .location = 0, .binding = 0, .format = gpu::PixelFormat::RGB32F, .offset = offsetof(Vertex, position) }, + { .location = 1, .binding = 0, .format = gpu::PixelFormat::RG32F, .offset = offsetof(Vertex, uv) } + }); } static constexpr auto binding_description() noexcept -> gpu::VertexBindingDescription { - return { 0, sizeof(Vertex), gpu::VertexInputRate::VERTEX }; + return { .binding = 0, .stride = sizeof(Vertex), .input_rate = gpu::VertexInputRate::VERTEX }; } }; -static constexpr auto VERTICES = std::array { - Vertex { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 3.f / 4.f } }, // -X side - { { -1.f, -1.f, 1.f }, { 1.f / 3.f, 3.f / 4.f } }, - { { -1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f } }, - { { -1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f } }, - { { -1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f } }, - { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 3.f / 4.f } }, - - { { -1.f, -1.f, -1.f }, { 1.f / 3.f, 2.f / 4.f } }, // -Z side - { { 1.f, 1.f, -1.f }, { 0.f, 1.f / 4.f } }, - { { 1.f, -1.f, -1.f }, { 0.f, 2.f / 4.f } }, - { { -1.f, -1.f, -1.f }, { 1.f / 3.f, 2.f / 4.f } }, - { { -1.f, 1.f, -1.f }, { 1.f / 3.f, 1.f / 4.f } }, - { { 1.f, 1.f, -1.f }, { 0.f, 1.f / 4.f } }, - - { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 2.f / 4.f } }, // -Y side - { { 1.f, -1.f, -1.f }, { 2.f / 3.f, 3.f / 4.f } }, - { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 3.f / 4.f } }, - { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 2.f / 4.f } }, - { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 3.f / 4.f } }, - { { -1.f, -1.f, 1.f }, { 1.f / 3.f, 2.f / 4.f } }, - - { { -1.f, 1.f, -1.f }, { 2.f / 3.f, 0.f } }, // +Y side - { { -1.f, 1.f, 1.f }, { 1.f / 3.f, 0.f } }, - { { 1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f / 4.f } }, - { { -1.f, 1.f, -1.f }, { 2.f / 3.f, 0.f } }, - { { 1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f / 4.f } }, - { { 1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f / 4.f } }, - - { { 1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f / 4.f } }, // +X side - { { 1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f / 4.f } }, - { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 2.f / 4.f } }, - { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 2.f / 4.f } }, - { { 1.f, -1.f, -1.f }, { 2.f / 3.f, 2.f / 4.f } }, - { { 1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f / 4.f } }, - - { { -1.f, 1.f, 1.f }, { 2.f / 3.f, 1.f / 4.f } }, // +Z side - { { -1.f, -1.f, 1.f }, { 2.f / 3.f, 2.f / 4.f } }, - { { 1.f, 1.f, 1.f }, { 1.f, 1.f / 4.f } }, - { { -1.f, -1.f, 1.f }, { 2.f / 3.f, 2.f / 4.f } }, - { { 1.f, -1.f, 1.f }, { 1.f, 2.f / 4.f } }, - { { 1.f, 1.f, 1.f }, { 1.f, 1.f / 4.f } }, -}; - struct ViewerData { - math::mat4f proj; - math::mat4f view; - math::mat4f model; + math::fmat4 proj; + math::fmat4 view; + math::fmat4 model; static constexpr auto layout_binding() -> gpu::DescriptorSetLayoutBinding { - return { 0, gpu::DescriptorType::UNIFORM_BUFFER, gpu::ShaderStageFlag::VERTEX, 1 }; + return { .binding = 0, + .type = gpu::DescriptorType::UNIFORM_BUFFER, + .stages = gpu::ShaderStageFlag::VERTEX, + .descriptor_count = 1 }; } }; -static constexpr auto VERTICES_SIZE = sizeof(Vertex) * stdr::size(VERTICES); +namespace { + const auto SHADER = stdfs::path { SHADER_DIR } / "textured_cube.spv"; + const auto TEXTURE = stdfs::path { RESOURCE_DIR } / "textures/cube.png"; + constexpr auto VERTICES = array { + Vertex { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 3.f / 4.f } }, // -X side + { { -1.f, -1.f, 1.f }, { 1.f / 3.f, 3.f / 4.f } }, + { { -1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f } }, + { { -1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f } }, + { { -1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f } }, + { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 3.f / 4.f } }, + + { { -1.f, -1.f, -1.f }, { 1.f / 3.f, 2.f / 4.f } }, // -Z side + { { 1.f, 1.f, -1.f }, { 0.f, 1.f / 4.f } }, + { { 1.f, -1.f, -1.f }, { 0.f, 2.f / 4.f } }, + { { -1.f, -1.f, -1.f }, { 1.f / 3.f, 2.f / 4.f } }, + { { -1.f, 1.f, -1.f }, { 1.f / 3.f, 1.f / 4.f } }, + { { 1.f, 1.f, -1.f }, { 0.f, 1.f / 4.f } }, + + { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 2.f / 4.f } }, // -Y side + { { 1.f, -1.f, -1.f }, { 2.f / 3.f, 3.f / 4.f } }, + { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 3.f / 4.f } }, + { { -1.f, -1.f, -1.f }, { 2.f / 3.f, 2.f / 4.f } }, + { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 3.f / 4.f } }, + { { -1.f, -1.f, 1.f }, { 1.f / 3.f, 2.f / 4.f } }, + + { { -1.f, 1.f, -1.f }, { 2.f / 3.f, 0.f } }, // +Y side + { { -1.f, 1.f, 1.f }, { 1.f / 3.f, 0.f } }, + { { 1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f / 4.f } }, + { { -1.f, 1.f, -1.f }, { 2.f / 3.f, 0.f } }, + { { 1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f / 4.f } }, + { { 1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f / 4.f } }, + + { { 1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f / 4.f } }, // +X side + { { 1.f, 1.f, 1.f }, { 1.f / 3.f, 1.f / 4.f } }, + { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 2.f / 4.f } }, + { { 1.f, -1.f, 1.f }, { 1.f / 3.f, 2.f / 4.f } }, + { { 1.f, -1.f, -1.f }, { 2.f / 3.f, 2.f / 4.f } }, + { { 1.f, 1.f, -1.f }, { 2.f / 3.f, 1.f / 4.f } }, + + { { -1.f, 1.f, 1.f }, { 2.f / 3.f, 1.f / 4.f } }, // +Z side + { { -1.f, -1.f, 1.f }, { 2.f / 3.f, 2.f / 4.f } }, + { { 1.f, 1.f, 1.f }, { 1.f, 1.f / 4.f } }, + { { -1.f, -1.f, 1.f }, { 2.f / 3.f, 2.f / 4.f } }, + { { 1.f, -1.f, 1.f }, { 1.f, 2.f / 4.f } }, + { { 1.f, 1.f, 1.f }, { 1.f, 1.f / 4.f } }, + }; -static constexpr auto BUFFERING_COUNT = 2; + constexpr auto VERTICES_SIZE = sizeof(Vertex) * stdr::size(VERTICES); + constexpr auto BUFFERING_COUNT = 2_u32; + constexpr auto POOL_SIZES = to_array({ + { + .type = gpu::DescriptorType::UNIFORM_BUFFER, + .descriptor_count = BUFFERING_COUNT, + }, + { + .type = gpu::DescriptorType::COMBINED_IMAGE_SAMPLER, + .descriptor_count = BUFFERING_COUNT, + } + }); + + constexpr auto OFFSETS = array { 0_u64 }; + constexpr auto PIPELINE_FLAGS = array { gpu::PipelineStageFlag::COLOR_ATTACHMENT_OUTPUT }; +} // namespace class Application: public base::Application { public: auto init_example() { - static constexpr auto POOL_SIZES = std::array { - gpu::DescriptorPool::Size { - .type = gpu::DescriptorType::UNIFORM_BUFFER, - .descriptor_count = BUFFERING_COUNT, - }, - gpu::DescriptorPool::Size { .type = gpu::DescriptorType::COMBINED_IMAGE_SAMPLER, - .descriptor_count = BUFFERING_COUNT } - }; - m_descriptor_pool = gpu::DescriptorPool::create(m_device, POOL_SIZES, BUFFERING_COUNT * 2) - .transform_error(monadic::assert("Failed to create descriptor pool")) - .value(); + m_descriptor_pool = TryAssert(gpu::DescriptorPool::create(m_device, POOL_SIZES, BUFFERING_COUNT * 2), + "Failed to create descriptor pool!"); // load shaders - m_vertex_shader = gpu::Shader::load_from_file(m_device, - SHADER_DIR "/textured_cube.spv", - gpu::ShaderStageFlag::VERTEX) - .transform_error(monadic::assert("Failed to load vertex shader")) - .value(); - - m_fragment_shader = gpu::Shader::load_from_file(m_device, - SHADER_DIR "/textured_cube.spv", - gpu::ShaderStageFlag::FRAGMENT) - .transform_error(monadic::assert("Failed to load fragment shader")) - .value(); - - m_descriptor_set_layout - = gpu::DescriptorSetLayout:: - create(m_device, - into_dyn_array(ViewerData::layout_binding(), - gpu::DescriptorSetLayoutBinding { - 1, - gpu::DescriptorType::COMBINED_IMAGE_SAMPLER, - gpu::ShaderStageFlag::FRAGMENT, - 1 })) - .transform_error(monadic::assert("Failed to create descriptor set layout")) - .value(); - m_pipeline_layout = gpu::PipelineLayout::create( - m_device, - { .descriptor_set_layouts = to_refs(m_descriptor_set_layout) }) - .transform_error(monadic::assert("Failed to create pipeline layout")) - .value(); + m_vertex_shader = TryAssert(gpu::Shader::load_from_file(m_device, SHADER, gpu::ShaderStageFlag::VERTEX), + std::format("Failed to load vertex shader {}!", SHADER.string())); + + m_fragment_shader = TryAssert(gpu::Shader::load_from_file(m_device, SHADER, gpu::ShaderStageFlag::FRAGMENT), + std::format("Failed to load fragment shader {}!", SHADER.string())); + + m_descriptor_set_layout = TryAssert(gpu::DescriptorSetLayout:: + create(m_device, + into_dynarray(ViewerData::layout_binding(), + gpu::DescriptorSetLayoutBinding { + 1, + gpu::DescriptorType::COMBINED_IMAGE_SAMPLER, + gpu::ShaderStageFlag::FRAGMENT, + 1 })), + "Failed to create descriptor set layout!"); + + m_pipeline_layout = TryAssert(gpu::PipelineLayout:: + create(m_device, + gpu::RasterPipelineLayout { + .descriptor_set_layouts = gpu::to_views(m_descriptor_set_layout) }), + "Failed to create pipeline layout!"); // initialize render pass const auto depth_format = [this] { const auto formats_properties = m_physical_device->formats_properties(); - const auto candidates = std::array { gpu::PixelFormat::DEPTH32F, - gpu::PixelFormat::DEPTH32F_STENCIL8U, - gpu::PixelFormat::DEPTH24_UNORM_STENCIL8U }; + const auto candidates = array { gpu::PixelFormat::DEPTH32F, + gpu::PixelFormat::DEPTH32F_STENCIL8U, + gpu::PixelFormat::DEPTH24_UNORM_STENCIL8U }; for (const auto format : candidates) { - const auto properties = stdr::find_if(formats_properties, - [format](const auto& pair) { - return pair.first == format; - }); + const auto properties = stdr::find_if(formats_properties, [format](const auto& pair) { + return pair.first == format; + }); ENSURES(properties != stdr::cend(formats_properties)); if (check_flag_bit(properties->second.optimal_tiling_features, gpu::FormatFeatureFlag::DEPTH_STENCIL_ATTACHMENT)) { @@ -193,7 +187,7 @@ class Application: public base::Application { } } - ensures(false, "No supported depth format found !"); + ensures(false, "No supported depth format found!"); std::unreachable(); }(); @@ -203,20 +197,6 @@ class Application: public base::Application { return flag; }(); - m_render_pass - = gpu::RenderPass:: - create(m_device, - { .attachments = { { - .format = m_swapchain->pixel_format(), - }, { - .format = depth_format, - .destination_layout = gpu::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL - }}, - .subpasses = { gpu::Subpass{ .bind_point = gpu::PipelineBindPoint::GRAPHICS, - .color_attachment_refs = { { .attachment_id = 0u }, }, .depth_attachment_ref = gpu::Subpass::Ref{.attachment_id = 1u, .layout = gpu::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, }, }, }, }) - .transform_error(monadic::assert("Failed to create render pass")) - .value(); - // initialize render pipeline const auto window_extent = m_window->extent(); const auto window_viewport = gpu::Viewport { @@ -230,159 +210,125 @@ class Application: public base::Application { }; const auto state = gpu::RasterPipelineState { - .input_assembly_state = { .topology = gpu::PrimitiveTopology::TRIANGLE_LIST, }, - .viewport_state = { .viewports = { window_viewport }, - .scissors = { scissor }, }, - .rasterization_state = { - .cull_mode = gpu::CullModeFlag::BACK, - .front_face = gpu::FrontFace::CLOCKWISE, - }, - .color_blend_state - = { .attachments = { { .blend_enable = true, - .src_color_blend_factor = gpu::BlendFactor::SRC_ALPHA, - .dst_color_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, - .src_alpha_blend_factor = gpu::BlendFactor::SRC_ALPHA, - .dst_alpha_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, - .alpha_blend_operation = gpu::BlendOperation::ADD, }, }, }, - .shader_state = to_refs(m_vertex_shader, m_fragment_shader), - .vertex_input_state = { - .binding_descriptions = into_dyn_array(Vertex::binding_description()), - .input_attribute_descriptions = to_dyn_array(Vertex::attribute_descriptions()), - }, - .depth_stencil_state = { - .depth_test_enable = true, - .depth_write_enable = true - }, - }; + .input_assembly_state = { .topology = gpu::PrimitiveTopology::TRIANGLE_LIST, }, + .viewport_state = { .viewports = { window_viewport }, + .scissors = { scissor }, }, + .rasterization_state = { + .cull_mode = gpu::CullModeFlag::BACK, + .front_face = gpu::FrontFace::CLOCKWISE, + }, + .color_blend_state + = { .attachments = { { .blend_enable = true, + .src_color_blend_factor = gpu::BlendFactor::SRC_ALPHA, + .dst_color_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, + .src_alpha_blend_factor = gpu::BlendFactor::SRC_ALPHA, + .dst_alpha_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, + .alpha_blend_operation = gpu::BlendOperation::ADD, }, }, }, + .shader_state = to_views(m_vertex_shader, m_fragment_shader), + .vertex_input_state = { + .binding_descriptions = into_dynarray(Vertex::binding_description()), + .input_attribute_descriptions = to_dynarray(Vertex::attribute_descriptions()), + }, + .depth_stencil_state = { + .depth_test_enable = true, + .depth_write_enable = true + }, + }; + + const auto rendering_info = gpu::RasterPipelineRenderingInfo { + .color_attachment_formats = { m_swapchain->pixel_format() }, + .depth_attachment_format = depth_format, + }; - m_pipeline = gpu::Pipeline::create(m_device, state, m_pipeline_layout, m_render_pass) - .transform_error(monadic::assert("Failed to create raster pipeline")) - .value(); + m_pipeline = TryAssert(gpu::Pipeline::create(m_device, + gpu::Pipeline::RasterizationCreateInfo { .state = as_ref(state), + .layout = m_pipeline_layout, + .rendering_info = rendering_info }), + "Failed to create raster pipeline!"); // load texture auto image = image::Image {}; - image.load_from_file(TEXTURE_DIR "/cube.png").transform_error(monadic::assert()).value(); + TryAssert(image.load_from_file(TEXTURE), std::format("Failed to load texture file {}!", TEXTURE.string())); - m_texture = gpu::Image::create(m_device, - { .extent = image.extent(), - .format = gpu::PixelFormat::RGBA8_UNORM, - .usages = gpu::ImageUsageFlag::SAMPLED - | gpu::ImageUsageFlag::TRANSFER_DST, - .property = gpu::MemoryPropertyFlag::DEVICE_LOCAL }) - .transform_error(monadic::assert("Failed to allocate texture")) - .value(); + m_texture = TryAssert(gpu::Image::create(m_device, + { .extent = image.extent(), + .format = gpu::PixelFormat::RGBA8_UNORM, + .usages = gpu::ImageUsageFlag::SAMPLED | gpu::ImageUsageFlag::TRANSFER_DST, + .properties = gpu::MemoryPropertyFlag::DEVICE_LOCAL }), + "Failed to allocate texture!"); { - auto staging_buffer - = gpu::Buffer::create(m_device, - { .usages = gpu::BufferUsageFlag::TRANSFER_SRC, - .size = image.size() }) - .transform_error(monadic::assert("Failed to allocate gpu texture staging buffer")) - .value(); - - staging_buffer.upload(image.data()) - .transform_error(monadic::assert("Failed to upload texture data to staging buffer")) - .value(); - - auto - cpy_fence = gpu::Fence::create(m_device) - .transform_error(monadic:: - assert("Failed to create copy texture buffer fence")) - .value(); - - const auto copy = { - gpu::BufferImageCopy { - .buffer_offset = 0, - .buffer_row_length = 0, - .buffer_image_height = 0, - .subresource_layers = {}, - .offset = {}, - .extent = image.extent() } - }; - auto copy_cmb = m_command_pool->create_command_buffer() - .transform_error(monadic:: - assert("Failed to allocate copy texture buffer " - "commandbuffer")) - .value(); - - copy_cmb.begin() - .transform_error(monadic::assert("Failed to begin texture upload command buffer")) - .value() - ->begin_debug_region("Upload texture data") - .transition_image_layout(m_texture, - gpu::ImageLayout::UNDEFINED, - gpu::ImageLayout::TRANSFER_DST_OPTIMAL) - .copy_buffer_to_image(staging_buffer, m_texture, as_view(copy)) - .transition_image_layout(m_texture, - gpu::ImageLayout::TRANSFER_DST_OPTIMAL, - gpu::ImageLayout::SHADER_READ_ONLY_OPTIMAL) - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to end texture upload command buffer")) - .value() - ->submit(m_raster_queue, {}, {}, {}, as_ref(cpy_fence)) - .transform_error(monadic::assert("Failed to submit texture upload command buffer")) - .value(); - - *cpy_fence.wait().transform_error(monadic::assert()); + auto cpy_fence = TryAssert(gpu::Fence::create(m_device), "Failed to create copy texture buffer fence!"); + auto staging_buffer = TryAssert(gpu::Buffer::create(m_device, + { .usages = gpu::BufferUsageFlag::TRANSFER_SRC, + .size = image.size() }), + "Failed to allocate gpu texture staging buffer!"); + TryAssert(staging_buffer.upload(image.data()), "Failed to upload texture data to staging buffer!"); + + auto copy_cmb = TryAssert(m_command_pool->create_command_buffer(), "Failed to allocate copy texture buffer"); + TryDiscardAssert((copy_cmb.record([&](auto cmb) noexcept { + const auto copy = array { + gpu::BufferImageCopy { + .buffer_offset = 0, + .buffer_row_length = 0, + .buffer_image_height = 0, + .subresource_layers = {}, + .offset = {}, + .extent = image.extent() } + }; + + cmb.begin_debug_region("Upload texture data") + .transition_image_layout(m_texture, + gpu::ImageLayout::UNDEFINED, + gpu::ImageLayout::TRANSFER_DST_OPTIMAL) + .copy_buffer_to_image(staging_buffer, m_texture, copy) + .end_debug_region() + .begin_debug_region("Transition texture data") + .transition_image_layout(m_texture, + gpu::ImageLayout::TRANSFER_DST_OPTIMAL, + gpu::ImageLayout::SHADER_READ_ONLY_OPTIMAL) + .end_debug_region(); + })), + "Failed to record texture upload and transition cmb!"); + + TryDiscardAssert(copy_cmb.submit(m_raster_queue, {}, {}, {}, cpy_fence), + "Failed to submit texture upload command buffer!"); + + TryDiscardAssert(cpy_fence.wait(), "Failed to create texture view!"); } - m_texture_view = gpu::ImageView::create(m_device, m_texture) - .transform_error(monadic::assert("Failed to create texture view")) - .value(); - - m_sampler = gpu::Sampler::create(m_device, {}) - .transform_error(monadic::assert("Failed to create sampler")) - .value(); - - // create present engine resources - m_submission_resources = std::vector {}; + m_texture_view = TryAssert(gpu::ImageView::create(m_device, { m_texture }), "Failed to create texture view!"); + m_sampler = TryAssert(gpu::Sampler::create(m_device, {}), "Failed to create sampler!"); + m_submission_resources = dynarray {}; m_submission_resources.reserve(BUFFERING_COUNT); for (auto _ : range(BUFFERING_COUNT)) { - m_submission_resources.push_back({ - .in_flight = gpu::Fence::create_signaled(m_device) - .transform_error(core::monadic:: - assert("Failed to create swapchain image " - "in flight fence")) - .value(), - .image_available = gpu::Semaphore::create(m_device) - .transform_error(core::monadic:: - assert("Failed to create present " - "wait semaphore")) - .value(), - .render_cmb = m_command_pool->create_command_buffer() - .transform_error(monadic::assert("Failed to create " - "transition command " - "buffers")) - .value(), - .viewer_buffer = gpu::Buffer::create(m_device, - { - .usages = gpu::BufferUsageFlag::UNIFORM, - .size = sizeof(ViewerData), - }, - true) - .transform_error(monadic:: - assert("Failed to allocate gpu viewer buffer")) - .value(), - .descriptor_set = m_descriptor_pool->create_descriptor_set(m_descriptor_set_layout) - .transform_error(monadic:: - assert("Failed to create descriptor set")) - .value(), - }); + m_submission_resources.push_back( + { .in_flight = TryAssert(gpu::Fence::create_signaled(m_device), "Failed to create swapchain image!"), + .image_available = TryAssert(gpu::Semaphore::create(m_device), "Failed to create present image!"), + .render_cmb = TryAssert(m_command_pool->create_command_buffer(), "Failed to create buffers!"), + .viewer_buffer = TryAssert(gpu::Buffer::create(m_device, + { + .usages = gpu::BufferUsageFlag::UNIFORM, + .size = sizeof(ViewerData), + .persistently_mapped = true, + }), + "Failed to allocate gpu viewer buffer!"), + .descriptor_set = TryAssert(m_descriptor_pool->create_descriptor_set(m_descriptor_set_layout), + "Failed to create descriptor set!") }); auto& res = m_submission_resources.back(); - const auto sets = std::array { + const auto sets = array { gpu::BufferDescriptor { .binding = 0, - .buffer = as_ref(res.viewer_buffer), + .buffer = res.viewer_buffer, .range = sizeof(ViewerData), .offset = 0, }, gpu::ImageDescriptor { .binding = 1, .layout = gpu::ImageLayout::SHADER_READ_ONLY_OPTIMAL, - .image_view = as_ref(m_texture_view), - .sampler = as_ref(m_sampler), + .image_view = m_texture_view, + .sampler = m_sampler, } }; res.descriptor_set.update(sets); @@ -390,163 +336,112 @@ class Application: public base::Application { const auto& images = m_swapchain->images(); - const auto image_count = stdr::size(images); - auto transition_cmbs - = m_command_pool->create_command_buffers(image_count) - .transform_error(monadic::assert("Failed to create transition command buffers")) - .value(); + const auto image_count = stdr::size(images); + auto transition_cmbs = TryAssert(m_command_pool->create_command_buffers(image_count), + "Failed to create transition command buffers!"); - m_image_resources = std::vector {}; + m_image_resources = dynarray {}; m_image_resources.reserve(stdr::size(images)); auto image_index = 0u; for (const auto& swap_image : images) { - auto view = gpu::ImageView::create(m_device, swap_image) - .transform_error(core::monadic:: - assert("Failed to create swapchain image view")) - .value(); - - auto depth_image - = gpu::Image::create(m_device, - gpu::Image::CreateInfo { - .extent = swap_image.extent(), - .format = depth_format, - .usages = gpu::ImageUsageFlag::DEPTH_STENCIL_ATTACHMENT, - .property = gpu::MemoryPropertyFlag::DEVICE_LOCAL }) - .transform_error(core::monadic::assert("Failed to create depth image")) - .value(); - - auto depth_view = gpu::ImageView::create(m_device, - depth_image, - gpu::ImageViewType::T2D, - { .aspect_mask = depth_aspect_flag }) - .transform_error(core::monadic:: - assert("Failed to create depth image view")) - .value(); - - auto framebuffer = m_render_pass - ->create_frame_buffer(m_device, - window_extent, - to_refs(view, depth_view)) - .transform_error(core::monadic::assert( - std::format("Failed to create framebuffer for image {}", - image_index))) - .value(); - - m_image_resources.push_back({ - .image = as_ref(swap_image), - .view = std::move(view), - .depth_image = std::move(depth_image), - .depth_view = std::move(depth_view), - .framebuffer = std::move(framebuffer), - .render_finished = gpu::Semaphore::create(m_device) - .transform_error(core::monadic::assert("Failed to create render " - "signal semaphore")) - .value(), - }); + auto view = TryAssert(gpu::ImageView::create(m_device, { swap_image }), "Failed to create swapchain image view!"); + auto depth_image = TryAssert(gpu::Image::create(m_device, + { .extent = swap_image.extent(), + .format = depth_format, + .usages = gpu::ImageUsageFlag::DEPTH_STENCIL_ATTACHMENT, + .properties = gpu::MemoryPropertyFlag::DEVICE_LOCAL }), + "Failed to create depth image!"); + + auto depth_view = TryAssert(gpu::ImageView::create(m_device, + { .image = depth_image, + .subresource_range = gpu:: + ImageSubresourceRange { .aspect_mask = depth_aspect_flag } }), + "Failed to create depth image view!"); + + m_image_resources.push_back({ .image = swap_image, + .view = std::move(view), + .depth_image = std::move(depth_image), + .depth_view = std::move(depth_view), + .render_finished = TryAssert(gpu::Semaphore::create(m_device), + "Failed to create render!") }); const auto& resources = m_image_resources.back(); auto& transition_cmb = transition_cmbs[image_index]; - *transition_cmb.begin(true) - .transform_error(monadic:: - assert("Failed to begin texture transition command buffer")) - .value() - ->begin_debug_region(std::format("transition image {}", image_index)) - .transition_image_layout(swap_image, - gpu::ImageLayout::UNDEFINED, - gpu::ImageLayout::PRESENT_SRC) - .transition_image_layout(resources.depth_image, - gpu::ImageLayout::UNDEFINED, - gpu::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - { .aspect_mask = depth_aspect_flag }) - .end_debug_region() - .end() - .transform_error(monadic:: - assert("Failed to begin texture transition command buffer")) - .transform(monadic::discard()); + TryDiscardAssert((transition_cmb.record([&](auto cmb) noexcept { + cmb.begin_debug_region(std::format("Transition image {}", image_index)) + .transition_image_layout(swap_image, + gpu::ImageLayout::UNDEFINED, + gpu::ImageLayout::PRESENT_SRC) + .end_debug_region() + .begin_debug_region(std::format("Transition depth image {}", image_index)) + .transition_image_layout(resources.depth_image, + gpu::ImageLayout::UNDEFINED, + gpu::ImageLayout::ATTACHMENT_OPTIMAL, + { .aspect_mask = depth_aspect_flag }) + .end_debug_region(); + })), + std::format("Failed to record transition cmb {}!", image_index)); ++image_index; } - const auto fence = gpu::Fence::create(m_device) - .transform_error(monadic::assert("Failed to create transition fence")) - .value(); + const auto fence = TryAssert(gpu::Fence::create(m_device), "Failed to create transition fence!"); - const auto cmbs = to_refs(transition_cmbs); - m_raster_queue->submit({ .command_buffers = cmbs }, as_ref(fence)) - .transform_error(monadic::assert("Failed to submit texture transition command buffers")) - .value(); + const auto cmbs = to_views(transition_cmbs); + TryDiscardAssert(m_raster_queue->submit({ .command_buffers = cmbs }, fence), + "Failed to submit texture transition command buffers!"); // setup vertex buffer - m_vertex_buffer = gpu::Buffer::create(m_device, - { .usages = gpu::BufferUsageFlag::VERTEX - | gpu::BufferUsageFlag::TRANSFER_DST, - .size = VERTICES_SIZE, - .property = gpu::MemoryPropertyFlag::DEVICE_LOCAL }) - .transform_error(monadic:: - assert("Failed to allocate gpu vertex buffer")) - .value(); + m_vertex_buffer = TryAssert(gpu::Buffer::create(m_device, + { .usages = gpu::BufferUsageFlag::VERTEX + | gpu::BufferUsageFlag::TRANSFER_DST, + .size = VERTICES_SIZE, + .properties = gpu::MemoryPropertyFlag::DEVICE_LOCAL }), + "Failed to allocate gpu vertex buffer!"); { - auto staging_buffer - = gpu::Buffer::create(m_device, - { .usages = gpu::BufferUsageFlag::TRANSFER_SRC, - .size = VERTICES_SIZE }) - .transform_error(monadic::assert("Failed to allocate gpu vertex staging buffer")) - .value(); - - staging_buffer.upload(VERTICES) - .transform_error(monadic::assert("Failed to upload vertex data to staging buffer")) - .value(); - - auto - cpy_fence = gpu::Fence::create(m_device) - .transform_error(monadic:: - assert("Failed to create copy vertex buffer fence")) - .value(); - - auto - copy_cmb = m_command_pool->create_command_buffer() - .transform_error(monadic::assert("Failed to allocate copy vertex buffer " - "commandbuffer")) - .value(); - - copy_cmb.begin() - .transform_error(monadic::assert("Failed to begin vertices upload command buffer")) - .value() - ->begin_debug_region("Upload vertex data to vertex buffer") + auto staging_buffer = TryAssert(gpu::Buffer::create(m_device, + { .usages = gpu::BufferUsageFlag::TRANSFER_SRC, + .size = VERTICES_SIZE }), + "Failed to allocate gpu vertex staging buffer!"); + + TryAssert(staging_buffer.upload(VERTICES), "Failed to upload vertex data to staging buffer!"); + + auto cpy_fence = TryAssert(gpu::Fence::create(m_device), "Failed to create copy vertex buffer fence!"); + + auto copy_cmb = TryAssert(m_command_pool->create_command_buffer(), "Failed to allocate copy vertex buffer"); + TryAssert(copy_cmb.begin(), "Failed to begin vertices upload command buffer"); + + copy_cmb.begin_debug_region("Upload vertex data to vertex buffer") .copy_buffer(staging_buffer, m_vertex_buffer, VERTICES_SIZE) - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to begin vertices upload command buffer")) - .value() - ->submit(m_raster_queue, {}, {}, {}, as_ref(cpy_fence)); + .end_debug_region(); - cpy_fence.wait().transform_error(monadic::assert()); + TryDiscardAssert(copy_cmb.end(), "Failed to begin vertices upload command buffer"); + TryDiscardAssert(copy_cmb.submit(m_raster_queue, {}, {}, {}, cpy_fence), + "Failed to submit vertices upload command buffer!"); + TryAssert(cpy_fence.wait(), "Failed to acquire next swapchain image!"); } - // wait for transition to be done - fence.wait().transform_error(monadic::assert()); + TryAssert(fence.wait(), ""); } auto run_example() { - using SecondF = stdc::duration; + LOG_MODULE.flush(); + + const auto current_time = clock::now(); + const auto window_extent = m_window->extent(); const auto window_extent_f32 = window_extent.to(); auto viewer_data = ViewerData { - .proj = math::perspective(math::radians(45.f), + .proj = math::perspective(math::angle::radians(45.f), window_extent_f32.width / window_extent_f32.height, 0.1f, 100.f), - .view = math::look_at(math::vec3f { 0.f, 3.f, 5.f }, - { 0.f, 0.f, 0.f }, - { 0.f, 1.f, 0.f }), - .model = math::mat4f::identity(), + .view = math::look_at(math::fvec3 { 0.f, 3.f, 5.f }, { 0.f, 0.f, 0.f }, { 0.f, 1.f, 0.f }), + .model = math::fmat4::identity(), }; - LOG_MODULE.flush(); - - const auto current_time = clock::now(); // get next swapchain image auto& submission_resource = m_submission_resources[m_current_frame]; @@ -554,89 +449,68 @@ class Application: public base::Application { const auto& wait = submission_resource.image_available; auto& in_flight = submission_resource.in_flight; - const auto acquire_next_image = bind_front(&gpu::SwapChain::acquire_next_image, - &*m_swapchain, - 100ms, - std::cref(wait)); - const auto extract_index = [](auto&& _result) static noexcept { - auto&& [result, _image_index] = _result; - return _image_index; - }; + TryAssert(in_flight.wait(), "Failed to wait in_flight fence!"); + TryAssert(in_flight.reset(), "Failed to reset in_flight fence!"); - const auto - image_index = in_flight.wait() - .transform([&in_flight](auto&&) noexcept { in_flight.reset(); }) - .and_then(acquire_next_image) - .transform(extract_index) - .transform_error(monadic:: - assert("Failed to acquire next swapchain image")) - .value(); + const auto&& [_, image_index] = TryAssert(m_swapchain->acquire_next_image(100ms, wait), + "Failed to acquire next swapchain image!"); const auto& swapchain_image_resource = m_image_resources[image_index]; - const auto& framebuffer = swapchain_image_resource.framebuffer; const auto& signal = swapchain_image_resource.render_finished; // update viewer data and upload - const auto time = stdc::duration_cast(current_time - m_start_time).count(); - viewer_data.model = math::rotate(math::mat4f::identity(), - time * math::radians(90.f), - math::vec3f { 0.f, 1.f, 0.f }); + const auto time = stdc::duration_cast(current_time - m_start_time).count(); + viewer_data.model = math::transpose(math::rotate(math::fmat4::identity(), + time * math::angle::radians(90.f), + math::fvec3 { 0.f, 1.f, 0.f })); auto& viewer_buffer = submission_resource.viewer_buffer; - viewer_buffer.upload(viewer_data); + TryAssert(viewer_buffer.upload(viewer_data), "Failed to upload texture to gpu!"); + + const auto rendering_info = gpu::RenderingInfo { + .render_area = { .x = 0, .y = 0, .width = window_extent.to().width, .height = window_extent.to().height }, + .color_attachments = { { .image_view = swapchain_image_resource.view, + .layout = gpu::ImageLayout::ATTACHMENT_OPTIMAL, + .clear_value = gpu::ClearColor { .color = colors::SILVER } } }, + .depth_attachment = { { .image_view = swapchain_image_resource.depth_view, + .layout = gpu::ImageLayout::ATTACHMENT_OPTIMAL, + .clear_value = gpu::ClearDepthStencil {} } } + }; // render in it auto& render_cmb = submission_resource.render_cmb; const auto& descriptor_set = submission_resource.descriptor_set; - static constexpr auto CLEAR_VALUES = std::array { - gpu::ClearColor { .color = RGBColorDef::SILVER }, - gpu::ClearDepthStencil {} - }; - static constexpr auto OFFSETS = std::array { 0_u64 }; - static constexpr auto PIPELINE_FLAGS = std::array { - gpu::PipelineStageFlag::COLOR_ATTACHMENT_OUTPUT - }; - - render_cmb.reset() - .transform_error(monadic::assert("Failed to reset render command buffer")) - .value() - ->begin() - .transform_error(monadic::assert("Failed to begin render command buffer")) - .value() - ->begin_debug_region("Render textured cube") - .begin_render_pass(m_render_pass, framebuffer, CLEAR_VALUES) - .bind_pipeline(m_pipeline) - .bind_vertex_buffers(to_refs(m_vertex_buffer), OFFSETS) - .bind_descriptor_sets(m_pipeline, m_pipeline_layout, as_refs(descriptor_set), {}) - .draw(stdr::size(VERTICES)) - .end_render_pass() - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to end render command buffer")) - .value() - ->submit(m_raster_queue, - as_refs(wait), - PIPELINE_FLAGS, - as_refs(signal), - as_ref(in_flight)) - .transform_error(monadic::assert("Failed to submit render command buffer")) - .value(); + TryAssert(render_cmb.reset(), std::format("Failed to reset render cmb {}!", image_index)); + TryDiscardAssert((render_cmb.record([&](auto cmb) noexcept { + cmb + .transition_image_layout(swapchain_image_resource.image, + gpu::ImageLayout::PRESENT_SRC, + gpu::ImageLayout::ATTACHMENT_OPTIMAL) + .begin_debug_region("Render cube") + .begin_rendering(rendering_info) + .bind_pipeline(m_pipeline) + .bind_vertex_buffers(gpu::as_views(m_vertex_buffer), OFFSETS) + .bind_descriptor_sets(m_pipeline, m_pipeline_layout, gpu::as_views(descriptor_set), {}) + .draw(stdr::size(VERTICES)) + .end_rendering() + .end_debug_region() + .transition_image_layout(swapchain_image_resource.image, + gpu::ImageLayout::ATTACHMENT_OPTIMAL, + gpu::ImageLayout::PRESENT_SRC); + })), + std::format("Failed to record render cmb {}!", image_index)); + TryDiscardAssert(render_cmb.submit(m_raster_queue, gpu::as_views(wait), PIPELINE_FLAGS, gpu::as_views(signal), in_flight), + "Failed to submit render command buffer!"); // present it - auto update_current_frame = [this](auto&&) mutable noexcept { - if (++m_current_frame >= BUFFERING_COUNT) m_current_frame = 0; - }; - - m_raster_queue->present(as_refs(m_swapchain), as_refs(signal), as_view(image_index)) - .transform(update_current_frame) - .transform_error(monadic::assert("Failed to present swapchain image")); + TryAssert(m_raster_queue->present(gpu::as_views(m_swapchain), gpu::as_views(signal), as_view(image_index)), + "Failed to present swapchain image!"); - m_raster_queue->wait_idle(); - m_device->wait_idle(); + if (++m_current_frame >= BUFFERING_COUNT) m_current_frame = 0; } - constexpr auto example_name() const noexcept -> std::string_view { return "Textured Cube"; } + constexpr auto example_name() const noexcept -> string_view { return "Textured Cube"; } private: DeferInit m_descriptor_pool; @@ -645,19 +519,18 @@ class Application: public base::Application { DeferInit m_fragment_shader; DeferInit m_descriptor_set_layout; DeferInit m_pipeline_layout; - DeferInit m_render_pass; DeferInit m_pipeline; DeferInit m_texture; DeferInit m_texture_view; DeferInit m_sampler; - std::vector m_submission_resources; - std::vector m_image_resources; + dynarray m_submission_resources; + dynarray m_image_resources; DeferInit m_vertex_buffer; usize m_current_frame = 0_usize; decltype(clock::now()) m_start_time = clock::now(); }; -auto main(std::span args) -> int { +auto main(array_view args) -> int { auto app = Application {}; app.run(args); return 0; diff --git a/examples/gpu/textured_cube/win32/manifest.manifest b/examples/gpu/textured_cube/win32/manifest.manifest deleted file mode 100644 index 4a9b5f6a6..000000000 --- a/examples/gpu/textured_cube/win32/manifest.manifest +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - PerMonitorV2 - - - diff --git a/examples/gpu/textured_cube/xmake.lua b/examples/gpu/textured_cube/xmake.lua index f16e33fd1..748a76f2e 100644 --- a/examples/gpu/textured_cube/xmake.lua +++ b/examples/gpu/textured_cube/xmake.lua @@ -1,47 +1,28 @@ -if is_plat("linux") then - add_requires( - "nzsl", - { configs = { toolchains = "gcc", runtimes = "stdc++_shared", fs_watcher = false, link = {} } } - ) -elseif is_plat("windows") then - add_requires("nzsl", { configs = { toolchains = "msvc", runtimes = "MD", fs_watcher = false, links = {} } }) -else - add_requires("nzsl", { configs = { fs_watcher = false, links = {} } }) +local runtimes +local toolchain +if is_plat("windows") then + runtimes = "MD" + toolchain = "msvc" +elseif is_plat("linux") then + runtimes = "stdc++_shared" + toolchain = "gcc" end +add_requires("nzsl", { configs = { fs_watcher = false, kind = "binary", toolchains = toolchain, runtimes = runtimes } }) target("textured_cube", function() - set_kind("binary") - set_languages("cxxlatest", "clatest") + add_rules("stormkit::example", "compile.shaders") - add_rules("stormkit.flags") - add_rules("platform.windows.subsystem.console") - -- add_rules("platform.windows.subsystem.windows") - - add_rules("compile.shaders") - add_deps("core", "main", "log", "wsi", "gpu", "stormkit") - - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end - - add_files("src/*.cpp", "../common/app.mpp") - add_files("shaders/*.nzsl") - if is_plat("windows") then add_files("win32/*.manifest") end - - add_includedirs("$(builddir)/shaders") + add_files("src/*.cpp", "src/*.cppm", "../common/app.cppm", "shaders/*.nzsl") if get_config("devmode") then - add_defines('SHADER_DIR="$(builddir)/shaders"') - add_defines('TEXTURE_DIR="$(builddir)/textures"') - set_rundir("$(projectdir)") + add_defines(format('RESOURCE_DIR="%s"', path.unix(path.join(os.projectdir(), "examples/gpu/textured_cube")))) + add_defines(format('SHADER_DIR="%s"', path.unix("$(builddir)/shaders"))) end + add_embeddirs("$(builddir)/shaders") + + if get_config("devmode") then set_rundir("$(projectdir)") end - -- add_cxflags("--embed-dir=$(builddir)/shaders", {tools = {"clang", "clangxx", "clang-cl", "gcc", "gxx"}}) - after_build(function(target) os.cp("examples/gpu/textured_cube/textures/", "$(builddir)") end) + add_packages("nzsl") set_group("examples/stormkit-gpu") end) diff --git a/examples/gpu/triangle/iOS/Info.plist b/examples/gpu/triangle/iOS/Info.plist deleted file mode 100644 index a62a3592d..000000000 --- a/examples/gpu/triangle/iOS/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - diff --git a/examples/gpu/triangle/iOS/LaunchScreen.storyboard b/examples/gpu/triangle/iOS/LaunchScreen.storyboard deleted file mode 100644 index 436820912..000000000 --- a/examples/gpu/triangle/iOS/LaunchScreen.storyboard +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/gpu/triangle/macOS/Info.plist b/examples/gpu/triangle/macOS/Info.plist deleted file mode 100644 index f68f66070..000000000 --- a/examples/gpu/triangle/macOS/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/gpu/triangle/shaders/triangle.nzsl b/examples/gpu/triangle/shaders/triangle.nzsl index 5f88feefe..e6b6d6b57 100644 --- a/examples/gpu/triangle/shaders/triangle.nzsl +++ b/examples/gpu/triangle/shaders/triangle.nzsl @@ -15,7 +15,7 @@ struct VertOut { } [entry(vert)] -fn main(input: VertIn) -> VertOut { +fn vert_main(input: VertIn) -> VertOut { let output: VertOut; let position: vec2[f32]; @@ -44,7 +44,7 @@ struct FragOut { } [entry(frag)] -fn main(input: VertOut) -> FragOut { +fn frag_main(input: VertOut) -> FragOut { let output: FragOut; output.color = vec4[f32](input.color, 1.); diff --git a/examples/gpu/triangle/src/logger.cppm b/examples/gpu/triangle/src/logger.cppm new file mode 100644 index 000000000..7aa06a6b7 --- /dev/null +++ b/examples/gpu/triangle/src/logger.cppm @@ -0,0 +1,15 @@ +// Copyright (C) 2024 Arthur LAURENT +// This file is subject to the license terms in the LICENSE file +// found in the top-level of this distribution + +module; + +#include + +export module gpu_app:logger; + +import std; + +import stormkit.log; + +export { IN_MODULE_LOGGER("Triangle"); } diff --git a/examples/gpu/triangle/src/main.cpp b/examples/gpu/triangle/src/main.cpp index 4f8bdfac8..84737b6c4 100644 --- a/examples/gpu/triangle/src/main.cpp +++ b/examples/gpu/triangle/src/main.cpp @@ -2,22 +2,22 @@ // This file is subject to the license terms in the LICENSE file // found in the top-level of this distribution +#include + import std; import stormkit; import gpu_app; -#include +#include #include -LOGGER("stormkit.examples.gpu.triangle"); - #ifndef SHADER_DIR - #define SHADER_DIR "../share/shaders" +static constexpr auto SHADER_DIR "../share/stormkit/shaders/" #endif -namespace stdr = std::ranges; -namespace stdfs = std::filesystem; + namespace stdr = std::ranges; +namespace stdfs = std::filesystem; using namespace std::literals; using namespace stormkit; @@ -29,43 +29,28 @@ struct SubmissionResource { }; struct SwapchainImageResource { - Ref image; - gpu::ImageView view; - gpu::FrameBuffer framebuffer; - gpu::Semaphore render_finished; + gpu::view::Image image; + gpu::ImageView view; + gpu::Semaphore render_finished; }; -static constexpr auto BUFFERING_COUNT = 2; +namespace { + constexpr auto BUFFERING_COUNT = 2_u32; + const auto SHADER = stdfs::path { SHADER_DIR } / "triangle.spv"; +} // namespace class Application: public base::Application { public: auto init_example() { // load shaders - m_vertex_shader = gpu::Shader::load_from_file(m_device, - SHADER_DIR "/triangle.spv", - gpu::ShaderStageFlag::VERTEX) - .transform_error(monadic::assert("Failed to load vertex shader")) - .value(); - - m_fragment_shader = gpu::Shader::load_from_file(m_device, - SHADER_DIR "/triangle.spv", - gpu::ShaderStageFlag::FRAGMENT) - .transform_error(monadic::assert("Failed to load fragment shader")) - .value(); - - m_pipeline_layout = gpu::PipelineLayout::create(m_device, {}) - .transform_error(monadic::assert("Failed to create pipeline layout")) - .value(); - - // initialize render pass - m_render_pass - = gpu::RenderPass:: - create(m_device, - { .attachments = { { .format = m_swapchain->pixel_format() } }, - .subpasses = { { .bind_point = gpu::PipelineBindPoint::GRAPHICS, - .color_attachment_refs = { { .attachment_id = 0u } } } } }) - .transform_error(monadic::assert("Failed to create render pass")) - .value(); + m_vertex_shader = TryAssert(gpu::Shader::load_from_file(m_device, SHADER, gpu::ShaderStageFlag::VERTEX), + std::format("Failed to load vertex shader {}!", SHADER.string())); + + m_fragment_shader = TryAssert(gpu::Shader::load_from_file(m_device, SHADER, gpu::ShaderStageFlag::FRAGMENT), + std::format("Failed to load fragment shader {}!", SHADER.string())); + + m_pipeline_layout = TryAssert(gpu::PipelineLayout::create(m_device, gpu::RasterPipelineLayout {}), + "Failed to create pipeline layoutu!"); const auto window_extent = m_window->extent(); @@ -81,41 +66,43 @@ class Application: public base::Application { }; const auto state = gpu::RasterPipelineState { - .input_assembly_state = { .topology = gpu::PrimitiveTopology::TRIANGLE_LIST, }, - .viewport_state = { .viewports = { window_viewport }, - .scissors = { scissor }, }, - .color_blend_state - = { .attachments = { { .blend_enable = true, - .src_color_blend_factor = gpu::BlendFactor::SRC_ALPHA, - .dst_color_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, - .src_alpha_blend_factor = gpu::BlendFactor::SRC_ALPHA, - .dst_alpha_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, - .alpha_blend_operation = gpu::BlendOperation::ADD, }, }, }, - .shader_state = to_refs(m_vertex_shader, m_fragment_shader), - }; - - m_pipeline = gpu::Pipeline::create(m_device, state, m_pipeline_layout, m_render_pass) - .transform_error(monadic::assert("Failed to create raster pipeline")) - .value(); + .input_assembly_state = { .topology = gpu::PrimitiveTopology::TRIANGLE_LIST, }, + .viewport_state = { .viewports = { window_viewport }, + .scissors = { scissor }, }, + .color_blend_state + = { .attachments = { { .blend_enable = true, + .src_color_blend_factor = gpu::BlendFactor::SRC_ALPHA, + .dst_color_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, + .src_alpha_blend_factor = gpu::BlendFactor::SRC_ALPHA, + .dst_alpha_blend_factor = gpu::BlendFactor::ONE_MINUS_SRC_ALPHA, + .alpha_blend_operation = gpu::BlendOperation::ADD, }, }, }, + .shader_state = gpu::to_views(m_vertex_shader, m_fragment_shader), + }; + + const auto rendering_info = gpu::RasterPipelineRenderingInfo { + .color_attachment_formats = { m_swapchain->pixel_format() } + }; + + m_pipeline = TryAssert(gpu::Pipeline::create(m_device, + gpu::Pipeline::RasterizationCreateInfo { .state = as_ref(state), + .layout = m_pipeline_layout, + .rendering_info = rendering_info }), + "Failed to create raster pipeline!"); // create present engine resources - m_submission_resources = init_by>([&](auto& out) noexcept { + m_submission_resources = init_by>([&](auto& out) noexcept { out.reserve(BUFFERING_COUNT); for (auto _ : range(BUFFERING_COUNT)) { out.push_back({ - .in_flight = gpu::Fence::create_signaled(m_device) - .transform_error(monadic:: - assert("Failed to create swapchain image " - "in flight fence")) - .value(), - .image_available = gpu::Semaphore::create(m_device) - .transform_error(monadic::assert("Failed to create " - "present wait semaphore")) - .value(), - .render_cmb = m_command_pool->create_command_buffer() - .transform_error(monadic::assert("Failed to create transition " - "command buffers")) - .value(), + .in_flight = TryAssert(gpu::Fence::create_signaled(m_device), + "Failed to create swapchain image " + "in flight fence!"), + .image_available = TryAssert(gpu::Semaphore::create(m_device), + "Failed to create " + "present wait semaphore!"), + .render_cmb = TryAssert(m_command_pool->create_command_buffer(), + "Failed to create transition " + "command buffers!"), }); } }); @@ -123,65 +110,43 @@ class Application: public base::Application { // transition swapchain image to present image const auto& images = m_swapchain->images(); - const auto image_count = stdr::size(images); - auto transition_cmbs - = m_command_pool->create_command_buffers(image_count) - .transform_error(monadic::assert("Failed to create transition command buffers")) - .value(); + const auto image_count = stdr::size(images); + auto transition_cmbs = TryAssert(m_command_pool->create_command_buffers(image_count), + "Failed to create transition command buffers!"); m_image_resources.reserve(stdr::size(images)); auto image_index = 0u; for (const auto& swap_image : images) { - auto view = gpu::ImageView::create(m_device, swap_image) - .transform_error(core::monadic:: - assert("Failed to create swapchain image view")) - .value(); - auto framebuffer = m_render_pass - ->create_frame_buffer(m_device, window_extent, to_refs(view)) - .transform_error(core::monadic::assert( - std::format("Failed to create framebuffer for image {}", - image_index))) - .value(); - - m_image_resources.push_back({ - .image = as_ref(swap_image), - .view = std::move(view), - .framebuffer = std::move(framebuffer), - .render_finished = gpu::Semaphore::create(m_device) - .transform_error(core::monadic::assert("Failed to create render " - "signal semaphore")) - .value(), - }); + auto view = TryAssert(gpu::ImageView::create(m_device, { swap_image }), "Failed to create swapchain image view!"); + + m_image_resources.push_back({ .image = swap_image, + .view = std::move(view), + .render_finished = TryAssert(gpu::Semaphore::create(m_device), + "Failed to create render " + "signal semaphore!") }); auto& transition_cmb = transition_cmbs[image_index]; - *transition_cmb.begin(true) - .transform_error(monadic:: - assert("Failed to begin texture transition command buffer")) - .value() - ->begin_debug_region(std::format("transition image {}", image_index)) - .transition_image_layout(swap_image, - gpu::ImageLayout::UNDEFINED, - gpu::ImageLayout::PRESENT_SRC) - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to begin texture transition command " - "buffer")); + TryDiscardAssert((transition_cmb.record([&](auto cmb) noexcept { + cmb.begin_debug_region(std::format("Transition image {}", image_index)) + .transition_image_layout(swap_image, + gpu::ImageLayout::UNDEFINED, + gpu::ImageLayout::PRESENT_SRC) + .end_debug_region(); + })), + std::format("Failed to record transition cmb {}!", image_index)); ++image_index; } - const auto fence = gpu::Fence::create(m_device) - .transform_error(monadic::assert("Failed to create transition fence")) - .value(); + const auto fence = TryAssert(gpu::Fence::create(m_device), "Failed to create transition fence!"); - const auto cmbs = to_refs(transition_cmbs); + const auto cmbs = gpu::to_views(transition_cmbs); - m_raster_queue->submit({ .command_buffers = cmbs }, as_ref(fence)) - .transform_error(monadic::assert("Failed to submit texture transition command buffers")) - .value(); + TryAssert(m_raster_queue->submit({ .command_buffers = cmbs }, fence), + "Failed to submit texture transition command buffers!"); // wait for transition to be done - fence.wait().transform_error(monadic::assert()); + TryAssert(fence.wait(), ""); } auto run_example() { @@ -193,80 +158,69 @@ class Application: public base::Application { const auto& wait = submission_resource.image_available; auto& in_flight = submission_resource.in_flight; - const auto acquire_next_image = bind_front(&gpu::SwapChain::acquire_next_image, - &*m_swapchain, - 100ms, - std::cref(wait)); - const auto extract_index = [](auto&& _result) static noexcept { - auto&& [result, _image_index] = _result; - return _image_index; - }; + TryAssert(in_flight.wait(), "Failed to wait in_flight fence!"); + TryAssert(in_flight.reset(), "Failed to reset in_flight fence!"); - const auto - image_index = in_flight.wait() - .transform([&in_flight](auto&&) mutable noexcept { in_flight.reset(); }) - .and_then(acquire_next_image) - .transform(extract_index) - .transform_error(monadic:: - assert("Failed to acquire next swapchain image")) - .value(); + const auto&& [_, image_index] = TryAssert(m_swapchain->acquire_next_image(100ms, wait), + "Failed to acquire next swapchain image!"); const auto& swapchain_image_resource = m_image_resources[image_index]; - const auto& framebuffer = swapchain_image_resource.framebuffer; const auto& signal = swapchain_image_resource.render_finished; - static constexpr auto PIPELINE_FLAGS = std::array { - gpu::PipelineStageFlag::COLOR_ATTACHMENT_OUTPUT + static constexpr auto PIPELINE_FLAGS = array { gpu::PipelineStageFlag::COLOR_ATTACHMENT_OUTPUT }; + + const auto window_extent = m_window->extent().to(); + const auto rendering_info = gpu::RenderingInfo { + .render_area = { .x = 0, .y = 0, .width = window_extent.width, .height = window_extent.height }, + .color_attachments = { { .image_view = swapchain_image_resource.view, + .layout = gpu::ImageLayout::ATTACHMENT_OPTIMAL, + .clear_value = gpu::ClearColor { .color = colors::SILVER } } } }; // render in it auto& render_cmb = submission_resource.render_cmb; - *render_cmb.reset() - .transform_error(monadic::assert("Failed to reset render command buffer")) - .value() - ->begin() - .transform_error(monadic::assert("Failed to begin render command buffer")) - .value() - ->begin_debug_region("Render triangle") - .begin_render_pass(m_render_pass, framebuffer) - .bind_pipeline(m_pipeline) - .draw(3) - .end_render_pass() - .end_debug_region() - .end() - .transform_error(monadic::assert("Failed to end render command buffer")) - .value() - ->submit(m_raster_queue, - as_refs(wait), - PIPELINE_FLAGS, - as_refs(signal), - as_ref(in_flight)) - .transform_error(monadic::assert("Failed to submit render command buffer")); + TryAssert(render_cmb.reset(), std::format("Failed to reset render cmb {}!", image_index)); + TryDiscardAssert((render_cmb.record([&](auto cmb) noexcept { + cmb + .transition_image_layout(swapchain_image_resource.image, + gpu::ImageLayout::PRESENT_SRC, + gpu::ImageLayout::ATTACHMENT_OPTIMAL) + .begin_debug_region("Render triangle") + .begin_rendering(rendering_info) + .bind_pipeline(m_pipeline) + .draw(3) + .end_rendering() + .end_debug_region() + .transition_image_layout(swapchain_image_resource.image, + gpu::ImageLayout::ATTACHMENT_OPTIMAL, + gpu::ImageLayout::PRESENT_SRC); + })), + std::format("Failed to record render cmb {}!", image_index)); + + TryDiscardAssert(render_cmb.submit(m_raster_queue, gpu::as_views(wait), PIPELINE_FLAGS, gpu::as_views(signal), in_flight), + "Failed to submit render command buffer"); // present it - auto update_current_frame = [this](auto&&) mutable noexcept { - if (++m_current_frame >= BUFFERING_COUNT) m_current_frame = 0; - }; + TryDiscardAssert(m_raster_queue->present(gpu::as_views(m_swapchain), gpu::as_views(signal), as_view(image_index)), + "Failed to present swapchain image!"); - m_raster_queue->present(as_refs(m_swapchain), as_refs(signal), as_view(image_index)) - .transform(update_current_frame) - .transform_error(monadic::assert("Failed to present swapchain image")); + if (++m_current_frame >= BUFFERING_COUNT) m_current_frame = 0; } - constexpr auto example_name() const noexcept -> std::string_view { return "Triangle"; } + constexpr auto example_name() const noexcept -> string_view { return "Triangle"; } private: - DeferInit m_vertex_shader; - DeferInit m_fragment_shader; - DeferInit m_pipeline_layout; - DeferInit m_render_pass; - DeferInit m_pipeline; - std::vector m_submission_resources; - std::vector m_image_resources; - usize m_current_frame = 0_usize; + DeferInit m_vertex_shader; + DeferInit m_fragment_shader; + DeferInit m_pipeline_layout; + DeferInit m_render_pass; + DeferInit m_pipeline; + dynarray m_submission_resources; + dynarray m_image_resources; + usize m_current_frame = 0_usize; }; -auto main(std::span args) -> int { +auto main(array_view args) -> int { auto app = Application {}; app.run(args); return 0; diff --git a/examples/gpu/triangle/win32/manifest.manifest b/examples/gpu/triangle/win32/manifest.manifest deleted file mode 100644 index 4a9b5f6a6..000000000 --- a/examples/gpu/triangle/win32/manifest.manifest +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - PerMonitorV2 - - - diff --git a/examples/gpu/triangle/xmake.lua b/examples/gpu/triangle/xmake.lua index 7069fe24b..762d686d0 100644 --- a/examples/gpu/triangle/xmake.lua +++ b/examples/gpu/triangle/xmake.lua @@ -1,46 +1,28 @@ -if is_plat("linux") then - add_requires( - "nzsl", - { configs = { toolchains = "gcc", runtimes = "stdc++_shared", fs_watcher = false, link = {} } } - ) -elseif is_plat("windows") then - add_requires("nzsl", { configs = { toolchains = "msvc", runtimes = "MD", fs_watcher = false, links = {} } }) -else - add_requires("nzsl", { configs = { fs_watcher = false, links = {} } }) +local runtimes +local toolchain +if is_plat("windows") then + runtimes = "MD" + toolchain = "msvc" +elseif is_plat("linux") then + runtimes = "stdc++_shared" + toolchain = "gcc" end +add_requires("nzsl", { configs = { fs_watcher = false, kind = "binary", toolchains = toolchain, runtimes = runtimes } }) target("triangle", function() - set_kind("binary") - set_languages("cxxlatest", "clatest") + add_rules("stormkit::example", "compile.shaders") - add_rules("stormkit.flags") - -- add_rules("platform.windows.subsystem.windows") - add_rules("platform.windows.subsystem.console") - - add_rules("compile.shaders") - add_deps("core", "main", "log", "wsi", "gpu", "stormkit") - - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end - - add_files("../common/app.mpp") - add_files("src/*.cpp") - add_files("shaders/*.nzsl") - if is_plat("windows") then add_files("win32/*.manifest") end - - add_includedirs("$(builddir)/shaders") + add_files("src/*.cpp", "src/*.cppm", "../common/app.cppm", "shaders/*.nzsl") if get_config("devmode") then - add_defines('SHADER_DIR="$(builddir)/shaders"') - set_rundir("$(projectdir)") + add_defines(format('RESOURCE_DIR="%s"', path.unix(path.join(os.projectdir(), "gpu/triangle")))) + add_defines(format('SHADER_DIR="%s"', path.unix("$(builddir)/shaders"))) end - add_embeddirs("$(builddir)/shaders") + if get_config("devmode") then set_rundir("$(projectdir)") end + + add_packages("nzsl") + set_group("examples/stormkit-gpu") end) diff --git a/examples/log/console-logger/iOS/info.plist b/examples/log/console-logger/iOS/info.plist deleted file mode 100644 index 83e13e582..000000000 --- a/examples/log/console-logger/iOS/info.plist +++ /dev/null @@ -1,44 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - diff --git a/examples/log/console-logger/macOS/info.plist b/examples/log/console-logger/macOS/info.plist deleted file mode 100644 index f68f66070..000000000 --- a/examples/log/console-logger/macOS/info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/log/console-logger/src/main.cpp b/examples/log/console-logger/src/main.cpp index 869b4f17f..8e78d5eb0 100644 --- a/examples/log/console-logger/src/main.cpp +++ b/examples/log/console-logger/src/main.cpp @@ -4,15 +4,14 @@ import std; -import stormkit.core; -import stormkit.log; +import stormkit; #include using namespace stormkit; struct Bar { - std::string d = "FooBar"; + string d = "FooBar"; }; template @@ -20,30 +19,35 @@ struct std::formatter: std::formatter, Char template auto format(const Bar& data, FormatContext& ctx) const -> decltype(ctx.out()) { auto&& out = ctx.out(); - return format_to(out, "[Bar: d = {}]", data.d); + return format_to(out, "[Bar d: {}]", data.d); } }; struct Foo { - u32 a = 0u; - float b = 2.3f; - Bar c = Bar {}; + u32 a = 0u; + f32 b = 2.3f; + Bar c = Bar {}; }; -template -struct std::formatter: std::formatter, CharT> { - template - auto format(const Foo& data, FormatContext& ctx) const -> decltype(ctx.out()) { - auto&& out = ctx.out(); - return format_to(out, "[Foo: a = {}, b = {}, c = {}]", data.a, data.b, data.c); - } -}; +template +auto format_as(const Foo& data, FormatContext& ctx) -> decltype(ctx.out()) { + return std::format_to(ctx.out(), "[Foo a: {}, b: {}, c: {}]", data.a, data.b, data.c); +} + +namespace stdr = std::ranges; +namespace stdv = std::views; //////////////////////////////////////// //////////////////////////////////////// -auto main(std::span) -> int { +auto main(array_view args) -> int { using log::operator""_module; + // force debug + auto args2 = dynarray { std::from_range, args }; + args2.emplace_back("--debug"); + + log::parse_args(args2); + auto logger = log::Logger::create_logger_instance(); log::Logger::ilog("This is an information"); diff --git a/examples/log/console-logger/xmake.lua b/examples/log/console-logger/xmake.lua index c7a024340..91bc43c2e 100644 --- a/examples/log/console-logger/xmake.lua +++ b/examples/log/console-logger/xmake.lua @@ -1,22 +1,7 @@ -target("console-logger") -do - set_kind("binary") - set_languages("cxxlatest", "clatest") +target("console-logger", function() + add_rules("stormkit::example") - add_rules("stormkit.flags") - add_rules("platform.windows.subsystem.console") - - add_deps("core", "main", "log") - - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end - - add_files("src/main.cpp") + add_files("src/*.cpp") set_group("examples/stormkit-log") -end +end) diff --git a/examples/log/file-logger/macOS/info.plist b/examples/log/file-logger/macOS/info.plist deleted file mode 100644 index f68f66070..000000000 --- a/examples/log/file-logger/macOS/info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/log/file-logger/src/main.cpp b/examples/log/file-logger/src/main.cpp index 79586a346..c8406c7cb 100644 --- a/examples/log/file-logger/src/main.cpp +++ b/examples/log/file-logger/src/main.cpp @@ -4,8 +4,7 @@ import std; -import stormkit.core; -import stormkit.log; +import stormkit; #include @@ -14,32 +13,27 @@ import stormkit.log; using namespace stormkit; struct Bar { - std::string d = "FooBar"; + string d = "FooBar"; }; template struct std::formatter: std::formatter, CharT> { template auto format(const Bar& data, FormatContext& ctx) const -> decltype(ctx.out()) { - auto&& out = ctx.out(); - return format_to(out, "[Bar: d = {}]", data.d); + return format_to(ctx.out(), "[Bar d: {}]", data.d); } }; struct Foo { stormkit::u32 a = 0u; - float b = 2.3f; + f32 b = 2.3f; Bar c = Bar {}; }; -template -struct std::formatter: std::formatter, CharT> { - template - auto format(const Foo& data, FormatContext& ctx) const -> decltype(ctx.out()) { - auto&& out = ctx.out(); - return format_to(out, "[Foo: a = {}, b = {}, c = {}]", data.a, data.b, data.c); - } -}; +template +auto format_as(const Foo& data, FormatContext& ctx) -> decltype(ctx.out()) { + return std::format_to(ctx.out(), "[Foo a: {}, b: {}, c: {}]", data.a, data.b, data.c); +} #ifdef STORMKIT_COMPILER_MSVC static const auto LOG_DIR = std::filesystem::path { L"log/" }; @@ -49,10 +43,15 @@ static const auto LOG_DIR = std::filesystem::path { "log/" }; //////////////////////////////////////// //////////////////////////////////////// -auto main(std::span _) -> int { - using namespace stormkit; +auto main(array_view args) -> int { using log::operator""_module; + // force debug + auto args2 = dynarray { std::from_range, args }; + args2.emplace_back("--debug"); + + log::parse_args(args2); + auto logger = log::Logger::create_logger_instance(LOG_DIR); log::Logger::ilog("This is an information"); diff --git a/examples/log/file-logger/xmake.lua b/examples/log/file-logger/xmake.lua index 44fd4c8f4..e5ef36883 100644 --- a/examples/log/file-logger/xmake.lua +++ b/examples/log/file-logger/xmake.lua @@ -1,22 +1,7 @@ -target("file-logger") -do - set_kind("binary") - set_languages("cxxlatest", "clatest") +target("file-logger", function() + add_rules("stormkit::example") - add_rules("stormkit.flags") - add_rules("platform.windows.subsystem.console") - - add_deps("core", "main", "log") - - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end - - add_files("src/main.cpp") + add_files("src/*.cpp") set_group("examples/stormkit-log") -end +end) diff --git a/examples/lua/wsi/events/lua/events.luau b/examples/lua/wsi/events/lua/events.luau new file mode 100644 index 000000000..9f8ed39ef --- /dev/null +++ b/examples/lua/wsi/events/lua/events.luau @@ -0,0 +1,111 @@ +local function main() + local window = wsi.window.open("test", 800, 600, wsi.window_flag.RESIZEABLE) + + window:on_closed(function() + print("Close event!") + return true + end) + + window:on_resized(function(extent) + print("Resize event:\n {}", extent) + end) + + window:on_monitor_changed(function(monitor) + print("Monitor changed event:\n {}", monitor) + end) + + window:on_mouse_moved(function(_, position) + print("Mouse move event:\n {}", position) + end) + + window:on_mouse_button_down(function(_, button, position) + print("Mouse button down event:\n {}\n {}", button, position) + end) + + window:on_mouse_button_up(function(_, button, position) + print("Mouse button up event:\n {}\n {}", button, position) + end) + + window:on_restored(function() + print("Restored event!") + end) + + window:on_minimized(function() + print("Minimzed event!") + end) + + window:on_activated(function() + print("Activate event!") + end) + + window:on_deactivated(function() + print("Deactivate event!") + end) + + local foo = 0 + window:on_key_down(function(_, key, c) + print("Key down event: {} {}", key, c) + + local closures = { + [wsi.key.ESCAPE] = function() + print("Closing window!") + window:close() + end, + [wsi.key.W] = function() + local extent = window:extent() + extent.width = extent.width + 10 + window:set_extent(extent) + end, + [wsi.key.T] = function() + foo = foo + 1 + window:set_title(format("StormKit Lua Events Example | T pressed {} times", foo)) + end, + [wsi.key.H] = function() + local extent = window:extent() + extent.height = extent.height + 10 + window:set_extent(extent) + end, + [wsi.key.F11] = function() + window:toggle_fullscreen() + print("Toggling fullscreen to {}", window:fullscreen()) + end, + [wsi.key.F1] = function() + window:toggle_hidden_mouse() + print("Toggling hidden mouse to {}", window:is_mouse_hidden()) + end, + [wsi.key.F2] = function() + window:toggle_locked_mouse() + print("Toggling locked mouse to {}", window:is_mouse_locked()) + end, + [wsi.key.F3] = function() + window:toggle_confined_mouse() + print("Toggling confined mouse to {}", window:is_mouse_confined()) + end, + [wsi.key.F4] = function() + window:toggle_relative_mouse() + print("Toggling relative mouse to {}", window:is_mouse_relative()) + end, + [wsi.key.F5] = function() + window:toggle_key_repeat() + print("Toggling key repeat to {}", window:is_key_repeat_enabled()) + end, + } + + for k, closure in pairs(closures) do + if k == key then + closure() + break + end + end + end) + + window:on_key_up(function(_, key, c) + print("Key up event: {} {}", key, c) + end) + + window:event_loop(function() + window:clear() + end) +end + +main() diff --git a/examples/lua/wsi/events/src/main.cpp b/examples/lua/wsi/events/src/main.cpp new file mode 100644 index 000000000..642a3c72d --- /dev/null +++ b/examples/lua/wsi/events/src/main.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2024 Arthur LAURENT +// This file is subject to the license terms in the LICENSE file +// found in the top-level of this distribution + +#include + +#include + +#include + +import std; + +import stormkit; + +LOGGER("lua-Events"); + +#ifndef RESOURCE_DIR + #define RESOURCE_DIR "../share/stormkit" +#endif + +namespace stdfs = std::filesystem; + +static const auto LUA_FILE = stdfs::path { RESOURCE_DIR } / stdfs::path { "lua/events.luau" }; + +using namespace stormkit; + +//////////////////////////////////////// +//////////////////////////////////////// +auto main(array_view args) -> int { + wsi::parse_args(args); + log::parse_args(args); + + auto logger = log::Logger::create_logger_instance(); + + lua::Engine::run(LUA_FILE, { .wsi = true }); + + return 0; +} diff --git a/examples/lua/wsi/events/xmake.lua b/examples/lua/wsi/events/xmake.lua new file mode 100644 index 000000000..28f72c434 --- /dev/null +++ b/examples/lua/wsi/events/xmake.lua @@ -0,0 +1,16 @@ +namespace("lua", function() + target("events", function() + add_rules("stormkit::example", "compile.shaders") + + add_files("src/*.cpp") + + if get_config("devmode") then + add_defines(format('RESOURCE_DIR="%s"', path.unix(path.join(os.projectdir(), "examples/lua/wsi/events")))) + end + add_embeddirs("$(builddir)/shaders") + + if get_config("devmode") then set_rundir("$(projectdir)") end + + set_group("examples/stormkit-lua") + end) +end) diff --git a/examples/wsi/events/iOS/Info.plist b/examples/wsi/events/iOS/Info.plist deleted file mode 100644 index 823db4f45..000000000 --- a/examples/wsi/events/iOS/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - diff --git a/examples/wsi/events/iOS/LaunchScreen.storyboard b/examples/wsi/events/iOS/LaunchScreen.storyboard deleted file mode 100644 index 94d24c83b..000000000 --- a/examples/wsi/events/iOS/LaunchScreen.storyboard +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/wsi/events/macOS/Info.plist b/examples/wsi/events/macOS/Info.plist deleted file mode 100644 index e8cc71539..000000000 --- a/examples/wsi/events/macOS/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/wsi/events/src/main.cpp b/examples/wsi/events/src/main.cpp index b43c56d2f..49cb38c3e 100644 --- a/examples/wsi/events/src/main.cpp +++ b/examples/wsi/events/src/main.cpp @@ -4,9 +4,7 @@ import std; -import stormkit.core; -import stormkit.log; -import stormkit.wsi; +import stormkit; #include #include @@ -20,8 +18,10 @@ namespace stdr = std::ranges; //////////////////////////////////////// //////////////////////////////////////// -auto main(std::span args) -> int { +auto main(array_view args) -> int { wsi::parse_args(args); + log::parse_args(args); + auto logger = log::Logger::create_logger_instance(); const auto monitors = wsi::get_monitors(); @@ -39,85 +39,72 @@ auto main(std::span args) -> int { }); auto foo = 0; - window - .on(wsi::ResizedEventFunc { [](const math::Extent2& extent) static noexcept { - ilog("Resize event: {}", extent); - } }, - wsi::MonitorChangedEventFunc { [](const wsi::Monitor& monitor) noexcept { - ilog("Monitor changed event: {}", monitor); - } }, - wsi::MouseMovedEventFunc { [](u8 /*id*/, const math::vec2i& position) noexcept { - ilog("Mouse move event: {}", position); - } }, - wsi::MouseButtonDownEventFunc { - [](u8 /*id*/, wsi::MouseButton button, const math::vec2i& position) noexcept { - ilog("Mouse button down event: {} {}", button, position); - } }, - wsi::MouseButtonUpEventFunc { - [](u8 /*id*/, wsi::MouseButton button, const math::vec2i& position) noexcept { - ilog("Mouse button up event: {} {}", button, position); - } }, - wsi::RestoredEventFunc { [] noexcept { ilog("Restored event"); } }, - wsi::MinimizedEventFunc { [] noexcept { ilog("Minimized event"); } }, - wsi::ActivateEventFunc { [] noexcept { ilog("Activate event"); } }, - wsi::DeactivateEventFunc { [] noexcept { ilog("Deactivate event"); } }, - wsi::KeyDownEventFunc { - [&window, &foo](u8 /*id*/, wsi::Key key, char c) mutable noexcept { - switch (key) { - case wsi::Key::ESCAPE: - window.close(); - ilog("Closing window"); - break; - case wsi::Key::W: { - auto extent = window.extent(); - extent.width += 10; - window.set_extent(extent); - } break; - case wsi::Key::T: { - window - .set_title(std::format("StormKit WSI Events Example | T pressed {} times", - ++foo)); - } break; - case wsi::Key::H: { - auto extent = window.extent(); - extent.height += 10; - window.set_extent(extent); - } break; - case wsi::Key::F11: - window.toggle_fullscreen(); - ilog("Toggling fullscreen to {}", window.fullscreen()); - break; - case wsi::Key::F1: - window.toggle_hidden_mouse(); - ilog("Toggling hidden mouse to {}", window.is_mouse_hidden()); - break; - case wsi::Key::F2: - window.toggle_locked_mouse(); - ilog("Toggling locked mouse to {}", window.is_mouse_locked()); - break; - case wsi::Key::F3: - window.toggle_confined_mouse(); - ilog("Toggling confined mouse to {}", window.is_mouse_confined()); - break; - case wsi::Key::F4: - window.toggle_relative_mouse(); - ilog("Toggling relative mouse to {}", window.is_mouse_relative()); - break; - case wsi::Key::F5: - window.toggle_key_repeat(); - ilog("Toggling key repeat to {}", window.is_key_repeat_enabled()); - break; - default: break; - } + window.on(wsi::ResizedEventFunc { [](const math::uextent2& extent) static noexcept { ilog("Resize event: {}", extent); } }, + wsi::MonitorChangedEventFunc { [](const wsi::Monitor& monitor) noexcept { + ilog("Monitor changed event: {}", monitor); + } }, + wsi::MouseMovedEventFunc { [](u8 /*id*/, const math::ivec2& position) noexcept { + ilog("Mouse move event: {}", position); + } }, + wsi::MouseButtonDownEventFunc { [](u8 /*id*/, wsi::MouseButton button, const math::ivec2& position) noexcept { + ilog("Mouse button down event: {} {}", button, position); + } }, + wsi::MouseButtonUpEventFunc { [](u8 /*id*/, wsi::MouseButton button, const math::ivec2& position) noexcept { + ilog("Mouse button up event: {} {}", button, position); + } }, + wsi::RestoredEventFunc { [] noexcept { ilog("Restored event"); } }, + wsi::MinimizedEventFunc { [] noexcept { ilog("Minimized event"); } }, + wsi::ActivateEventFunc { [] noexcept { ilog("Activate event"); } }, + wsi::DeactivateEventFunc { [] noexcept { ilog("Deactivate event"); } }, + wsi::KeyDownEventFunc { [&window, &foo](u8 /*id*/, wsi::Key key, char c) mutable noexcept { + switch (key) { + case wsi::Key::ESCAPE: + window.close(); + ilog("Closing window"); + break; + case wsi::Key::W: { + auto extent = window.extent(); + extent.width += 10; + window.set_extent(extent); + } break; + case wsi::Key::T: { + window.set_title(std::format("StormKit WSI Events Example | T pressed {} times", ++foo)); + } break; + case wsi::Key::H: { + auto extent = window.extent(); + extent.height += 10; + window.set_extent(extent); + } break; + case wsi::Key::F11: + window.toggle_fullscreen(); + ilog("Toggling fullscreen to {}", window.fullscreen()); + break; + case wsi::Key::F1: + window.toggle_hidden_mouse(); + ilog("Toggling hidden mouse to {}", window.is_mouse_hidden()); + break; + case wsi::Key::F2: + window.toggle_locked_mouse(); + ilog("Toggling locked mouse to {}", window.is_mouse_locked()); + break; + case wsi::Key::F3: + window.toggle_confined_mouse(); + ilog("Toggling confined mouse to {}", window.is_mouse_confined()); + break; + case wsi::Key::F4: + window.toggle_relative_mouse(); + ilog("Toggling relative mouse to {}", window.is_mouse_relative()); + break; + case wsi::Key::F5: + window.toggle_key_repeat(); + ilog("Toggling key repeat to {}", window.is_key_repeat_enabled()); + break; + default: break; + } - ilog("Key down --\n code: {}\n value: '{}'\n raw_value: 0x{:0x})", - key, - c, - c); - } }, - wsi::KeyUpEventFunc { [](u8 /*id*/, wsi::Key key, char /*c*/) noexcept { - ilog("Key up --\n code: {}", key); - } }); + ilog("Key down --\n code: {}\n value: '{}'\n raw_value: 0x{:0x})", key, c, c); + } }, + wsi::KeyUpEventFunc { [](u8 /*id*/, wsi::Key key, char /*c*/) noexcept { ilog("Key up --\n code: {}", key); } }); window.event_loop([&] mutable { window.clear(); }); diff --git a/examples/wsi/events/xmake.lua b/examples/wsi/events/xmake.lua index 6713719ad..0340337ff 100644 --- a/examples/wsi/events/xmake.lua +++ b/examples/wsi/events/xmake.lua @@ -1,22 +1,7 @@ target("events", function() - set_kind("binary") - set_languages("cxxlatest", "clatest") + add_rules("stormkit::example") - add_rules("stormkit.flags") - add_rules("platform.windows.subsystem.console") - - add_deps("core", "main", "log", "wsi") - - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end - - add_files("src/main.cpp") - if is_plat("windows") then add_files("win32/*.manifest") end + add_files("src/*.cpp") set_group("examples/stormkit-wsi") end) diff --git a/examples/wsi/framebuffer/iOS/Info.plist b/examples/wsi/framebuffer/iOS/Info.plist deleted file mode 100644 index 823db4f45..000000000 --- a/examples/wsi/framebuffer/iOS/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - - diff --git a/examples/wsi/framebuffer/iOS/LaunchScreen.storyboard b/examples/wsi/framebuffer/iOS/LaunchScreen.storyboard deleted file mode 100644 index 94d24c83b..000000000 --- a/examples/wsi/framebuffer/iOS/LaunchScreen.storyboard +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/wsi/framebuffer/macOS/Info.plist b/examples/wsi/framebuffer/macOS/Info.plist deleted file mode 100644 index e8cc71539..000000000 --- a/examples/wsi/framebuffer/macOS/Info.plist +++ /dev/null @@ -1,36 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLongVersionString - ${MACOSX_BUNDLE_LONG_VERSION_STRING} - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - - diff --git a/examples/wsi/framebuffer/src/main.cpp b/examples/wsi/framebuffer/src/main.cpp index 0560211c6..ceaf590ba 100644 --- a/examples/wsi/framebuffer/src/main.cpp +++ b/examples/wsi/framebuffer/src/main.cpp @@ -4,9 +4,7 @@ import std; -import stormkit.core; -import stormkit.log; -import stormkit.wsi; +import stormkit; #include #include @@ -18,9 +16,7 @@ using namespace std::literals; namespace stdr = std::ranges; -auto update_pixels(stormkit::ThreadPool& pool, - std::vector>& pixels, - const auto& extent) noexcept { +auto update_pixels(stormkit::ThreadPool& pool, dynarray& pixels, const auto& extent) noexcept { const auto rect_width = extent.width / 5; const auto rect_height = extent.height / 5; @@ -33,32 +29,30 @@ auto update_pixels(stormkit::ThreadPool& pool, pixels.resize(extent.height * extent.width, colors::RED); - auto data = std::mdspan { stdr::data(pixels), extent.height, extent.width }; - parallel_for(pool, - range(extent.width * extent.height), - [&rect, data, &extent](auto x_y) mutable noexcept { - const auto x = as(x_y % extent.width); - const auto y = as(x_y / extent.width); - - if (math::AABB({ as(x), as(y) }, rect)) - data[y, x] = colors::BLACK; - else { - const auto color_id = as(x) / as(extent.width); - if (color_id >= 0.8) data[y, x] = colors::BLUE; - else if (color_id >= 0.6) - data[y, x] = colors::GREEN; - else if (color_id >= 0.4) - data[y, x] = colors::WHITE; - else if (color_id >= 0.2) - data[y, x] = colors::YELLOW; - else - data[y, x] = colors::RED; - } - }); + auto data = mdarray_view { stdr::data(pixels), extent.height, extent.width }; + parallel_for(pool, range(extent.width * extent.height), [&rect, data, &extent](auto x_y) mutable noexcept { + const auto x = as(x_y % extent.width); + const auto y = as(x_y / extent.width); + + if (math::AABB(math::uvec2 { as(x), as(y) }, rect)) data[y, x] = colors::BLACK; + else { + const auto color_id = as(x) / as(extent.width); + if (color_id >= 0.8) data[y, x] = colors::BLUE; + else if (color_id >= 0.6) + data[y, x] = colors::GREEN; + else if (color_id >= 0.4) + data[y, x] = colors::WHITE; + else if (color_id >= 0.2) + data[y, x] = colors::YELLOW; + else + data[y, x] = colors::RED; + } + }); } -auto main(std::span args) -> int { +auto main(array_view args) -> int { wsi::parse_args(args); + log::parse_args(args); auto logger = log::Logger::create_logger_instance(); @@ -66,25 +60,22 @@ auto main(std::span args) -> int { ilog("--- Monitors ---"); ilog("{}", monitors); - auto window = wsi::Window::open("Hello world", - { .width = 800_u32, .height = 600_u32 }, - wsi::WindowFlag::RESIZEABLE); + auto window = wsi::Window::open("Hello world", { .width = 800_u32, .height = 600_u32 }, wsi::WindowFlag::RESIZEABLE); ilog("wm: {}", window.wm()); auto pool = core::ThreadPool {}; - auto pixels = std::vector> {}; + auto pixels = dynarray {}; update_pixels(pool, pixels, window.extent()); auto active = true; - window - .on(wsi::ResizedEventFunc { [&](const math::Extent2& extent) mutable noexcept { - update_pixels(pool, pixels, extent); - window.fill_framebuffer(pixels); - } }, - wsi::RestoredEventFunc { [&active] mutable noexcept { active = true; } }, - wsi::MinimizedEventFunc { [&active] mutable noexcept { active = false; } }, - wsi::KeyDownEventFunc { [&window](u8 /*id*/, wsi::Key key, char /*c*/) mutable noexcept { - if (key == wsi::Key::ESCAPE) window.close(); - } }); + window.on(wsi::ResizedEventFunc { [&](const math::uextent2& extent) mutable noexcept { + update_pixels(pool, pixels, extent); + window.fill_framebuffer(pixels); + } }, + wsi::RestoredEventFunc { [&active] mutable noexcept { active = true; } }, + wsi::MinimizedEventFunc { [&active] mutable noexcept { active = false; } }, + wsi::KeyDownEventFunc { [&window](u8 /*id*/, wsi::Key key, char /*c*/) mutable noexcept { + if (key == wsi::Key::ESCAPE) window.close(); + } }); window.event_loop([&] noexcept { if (not active) std::this_thread::yield(); diff --git a/examples/wsi/framebuffer/win32/manifest.manifest b/examples/wsi/framebuffer/win32/manifest.manifest deleted file mode 100644 index f2708ecb1..000000000 --- a/examples/wsi/framebuffer/win32/manifest.manifest +++ /dev/null @@ -1,9 +0,0 @@ - - - - - true - PerMonitorV2 - - - diff --git a/examples/wsi/framebuffer/xmake.lua b/examples/wsi/framebuffer/xmake.lua index 0d9bb9906..ecb3674f3 100644 --- a/examples/wsi/framebuffer/xmake.lua +++ b/examples/wsi/framebuffer/xmake.lua @@ -1,22 +1,7 @@ target("framebuffer", function() - set_kind("binary") - set_languages("cxxlatest", "clatest") + add_rules("stormkit::example") - add_rules("stormkit.flags") - add_rules("platform.windows.subsystem.console") - - add_deps("core", "main", "log", "wsi") - - if is_mode("debug") then - add_defines("STORMKIT_BUILD_DEBUG") - add_defines("STORMKIT_ASSERT=1") - set_suffixname("-d") - else - add_defines("STORMKIT_ASSERT=0") - end - - add_files("src/main.cpp") - if is_plat("windows") then add_files("win32/*.manifest") end + add_files("src/*.cpp") set_group("examples/stormkit-wsi") end) diff --git a/include/status-code/boost_error_code.hpp b/include/status-code/boost_error_code.hpp new file mode 100644 index 000000000..749c7949f --- /dev/null +++ b/include/status-code/boost_error_code.hpp @@ -0,0 +1,343 @@ +/* Proposed SG14 status_code +(C) 2018 - 2022 Niall Douglas (5 commits) +File Created: Mar 2022 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_BOOST_ERROR_CODE_HPP +#define SYSTEM_ERROR2_BOOST_ERROR_CODE_HPP + +#ifndef SYSTEM_ERROR2_NOT_POSIX +#include "posix_code.hpp" +#endif + +#if defined(_WIN32) || defined(STANDARDESE_IS_IN_THE_HOUSE) +#include "win32_code.hpp" +#endif + +#include "std_error_code.hpp" + +#include +#include + +#include + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +class _boost_error_code_domain; +//! A `status_code` representing exactly a `boost::system::error_code` +using boost_error_code = status_code<_boost_error_code_domain>; + +namespace mixins +{ + template struct mixin : public Base + { + using Base::Base; + + //! Implicit constructor from a `boost::system::error_code` + inline mixin(boost::system::error_code ec); + + //! Returns the error code category + inline const boost::system::error_category &category() const noexcept; + }; +} // namespace mixins + + +/*! The implementation of the domain for `boost::system::error_code` error codes. + */ +class _boost_error_code_domain final : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + using _error_code_type = boost::system::error_code; + using _error_category_type = boost::system::error_category; + + std::string _name; + + static _base::string_ref _make_string_ref(int &errcode, _error_code_type c) noexcept + { +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) + try +#endif + { + std::string msg = c.message(); + return _base::atomic_refcounted_string_ref(msg.c_str(), msg.size()); + } +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) + catch(...) + { + errcode = ENOMEM; + return _base::string_ref("failed to allocate message"); + } +#endif + } + +public: + //! The value type of the `boost::system::error_code` code, which stores the `int` from the + //! `boost::system::error_code` + using value_type = int; + using _base::string_ref; + + //! Returns the error category singleton pointer this status code domain represents + const _error_category_type &error_category() const noexcept + { + auto ptr = 0x0ea88ff382d94915 ^ this->id(); + return *reinterpret_cast(ptr); + } + + //! Default constructor + explicit _boost_error_code_domain(const _error_category_type &category) noexcept + : _base(0x0ea88ff382d94915 ^ reinterpret_cast<_base::unique_id_type>(&category)) + , _name("boost_error_code_domain(") + { + _name.append(category.name()); + _name.push_back(')'); + } + _boost_error_code_domain(const _boost_error_code_domain &) = default; + _boost_error_code_domain(_boost_error_code_domain &&) = default; + _boost_error_code_domain &operator=(const _boost_error_code_domain &) = default; + _boost_error_code_domain &operator=(_boost_error_code_domain &&) = default; + ~_boost_error_code_domain() = default; + + static inline const _boost_error_code_domain *get(_error_code_type ec); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref(_name.c_str(), _name.size()); + return 0; + } // NOLINT + + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override; + virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override; + virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override; + virtual int _do_message(_vtable_message_args &args) const noexcept override; +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override; +#endif +}; + +namespace detail +{ + extern inline _boost_error_code_domain * + boost_error_code_domain_from_category(const boost::system::error_category &category) + { + static constexpr size_t max_items = 64; + static struct storage_t + { + std::atomic _lock; + union item_t + { + int _init; + _boost_error_code_domain domain; + constexpr item_t() + : _init(0) + { + } + ~item_t() {} + } items[max_items]; + size_t count{0}; + + void lock() + { + while(_lock.exchange(1, std::memory_order_acquire) != 0) + ; + } + void unlock() { _lock.store(0, std::memory_order_release); } + + storage_t() {} + ~storage_t() + { + lock(); + for(size_t n = 0; n < count; n++) + { + items[n].domain.~_boost_error_code_domain(); + } + unlock(); + } + _boost_error_code_domain *add(const boost::system::error_category &category) + { + _boost_error_code_domain *ret = nullptr; + lock(); + for(size_t n = 0; n < count; n++) + { + if(items[n].domain.error_category() == category) + { + ret = &items[n].domain; + break; + } + } + if(ret == nullptr && count < max_items) + { + ret = new(SYSTEM_ERROR2_ADDRESS_OF(items[count++].domain)) _boost_error_code_domain(category); + } + unlock(); + return ret; + } + } storage; + return storage.add(category); + } +} // namespace detail + +namespace mixins +{ + template + inline mixin::mixin(boost::system::error_code ec) + : Base(typename Base::_value_type_constructor{}, _boost_error_code_domain::get(ec), ec.value()) + { + } + + template + inline const boost::system::error_category &mixin::category() const noexcept + { + const auto &domain = static_cast(this->domain()); + return domain.error_category(); + }; +} // namespace mixins + +inline const _boost_error_code_domain *_boost_error_code_domain::get(boost::system::error_code ec) +{ + auto *p = detail::boost_error_code_domain_from_category(ec.category()); + assert(p != nullptr); + if(p == nullptr) + { + abort(); + } + return p; +} + + +SYSTEM_ERROR2_CONSTEXPR20 inline bool +_boost_error_code_domain::_do_failure(const status_code &code) const noexcept +{ + assert(code.domain() == *this); + return static_cast(code).value() != 0; // NOLINT +} + +inline bool _boost_error_code_domain::_do_equivalent(const status_code &code1, + const status_code &code2) const noexcept +{ + assert(code1.domain() == *this); + const auto &c1 = static_cast(code1); // NOLINT + const auto &cat1 = c1.category(); + // Are we comparing to another wrapped error_code? + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + const auto &cat2 = c2.category(); + // If the error code categories are identical, do literal comparison + if(cat1 == cat2) + { + return c1.value() == c2.value(); + } + // Otherwise fall back onto the _generic_code comparison, which uses default_error_condition() + return false; + } + // Am I an error code with generic category? + if(cat1 == boost::system::generic_category()) + { + // Convert to generic code, and compare that + generic_code _c1(static_cast(c1.value())); + return _c1 == code2; + } + // Am I an error code with system category? + if(cat1 == boost::system::system_category()) + { +// Convert to POSIX or Win32 code, and compare that +#ifdef _WIN32 + win32_code _c1((win32::DWORD) c1.value()); + return _c1 == code2; +#elif !defined(SYSTEM_ERROR2_NOT_POSIX) + posix_code _c1(c1.value()); + return _c1 == code2; +#endif + } + return false; +} + +inline void _boost_error_code_domain::_do_generic_code(_vtable_generic_code_args &args) const noexcept +{ + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + // Ask my embedded error code for its mapping to boost::system::errc, which is a subset of our generic_code errc. + boost::system::error_condition cond(c.category().default_error_condition(c.value())); + if(cond.category() == boost::system::generic_category()) + { + args.ret = generic_code(static_cast(cond.value())); + return; + } +#if !defined(SYSTEM_ERROR2_NOT_POSIX) && !defined(_WIN32) + if(cond.category() == boost::system::system_category()) + { + args.ret = generic_code(static_cast(cond.value())); + return; + } +#endif + args.ret = errc::unknown; +} + +inline int _boost_error_code_domain::_do_message(_vtable_message_args &args) const noexcept +{ + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + int ret = 0; + args.ret = _make_string_ref(ret, _error_code_type(c.value(), c.category())); + return ret; +} + +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) +SYSTEM_ERROR2_NORETURN inline void _boost_error_code_domain::_do_throw_exception(const status_code &code) const +{ + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + throw boost::system::system_error(boost::system::error_code(c.value(), c.category())); +} +#endif + +static_assert(sizeof(boost_error_code) <= sizeof(void *) * 2, "boost_error_code does not fit into a system_code!"); + +SYSTEM_ERROR2_NAMESPACE_END + +// Enable implicit construction of `boost_error_code` from `boost::system::error_code`. +namespace boost +{ + namespace system + { + inline SYSTEM_ERROR2_NAMESPACE::erased_status_code make_status_code(error_code c) noexcept + { + if(c.category() == detail::interop_category()) + { + // This is actually a wrap of std::error_code. If this fails to compile, your Boost is too old. + return std::make_status_code(std::error_code(c)); + } + return SYSTEM_ERROR2_NAMESPACE::boost_error_code(c); + } + } // namespace system +} // namespace boost + +#endif diff --git a/include/status-code/com_code.hpp b/include/status-code/com_code.hpp new file mode 100644 index 000000000..bf8726b4c --- /dev/null +++ b/include/status-code/com_code.hpp @@ -0,0 +1,269 @@ +/* Proposed SG14 status_code +(C) 2018 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_COM_CODE_HPP +#define SYSTEM_ERROR2_COM_CODE_HPP + +#if !defined(_WIN32) && !defined(STANDARDESE_IS_IN_THE_HOUSE) +#error This file should only be included on Windows +#endif + +#include "nt_code.hpp" +#include "win32_code.hpp" + +#ifndef STANDARDESE_IS_IN_THE_HOUSE +#include // needed by mingw for comdef.h to work + +#include +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +class _com_code_domain; +/*! (Windows only) A COM error code. Note semantic equivalence testing is only implemented for `FACILITY_WIN32` +and `FACILITY_NT_BIT`. As you can see at +[https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/](https://blogs.msdn.microsoft.com/eldar/2007/04/03/a-lot-of-hresult-codes/), +there are an awful lot of COM error codes, and keeping mapping tables for all of them would be impractical (for the +Win32 and NT facilities, we actually reuse the mapping tables in `win32_code` and `nt_code`). You can, of course, +inherit your own COM code domain from this one and override the `_do_equivalent()` function to add semantic equivalence +testing for whichever extra COM codes that your application specifically needs. +*/ +using com_code = status_code<_com_code_domain>; +//! (Windows only) A specialisation of `status_error` for the COM error code domain. +using com_error = status_error<_com_code_domain>; + +/*! (Windows only) The implementation of the domain for COM error codes and/or `IErrorInfo`. + */ +class _com_code_domain : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + + //! Construct from a `HRESULT` error code +#ifdef _COMDEF_NOT_WINAPI_FAMILY_DESKTOP_APP + static _base::string_ref _make_string_ref(int &errcode, HRESULT c, wchar_t *perrinfo = nullptr) noexcept +#else + static _base::string_ref _make_string_ref(int &errcode, HRESULT c, IErrorInfo *perrinfo = nullptr) noexcept +#endif + { + _com_error ce(c, perrinfo); +#ifdef _UNICODE + win32::DWORD wlen = (win32::DWORD) wcslen(ce.ErrorMessage()); + size_t allocation = wlen + (wlen >> 1); + win32::DWORD bytes; + if(wlen == 0) + { + errcode = ENOENT; + return _base::string_ref("failed to get message from system"); + } + for(;;) + { + auto *p = static_cast(malloc(allocation)); // NOLINT + if(p == nullptr) + { + errcode = ENOMEM; + return _base::string_ref("failed to get message from system"); + } + bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, ce.ErrorMessage(), (int) (wlen + 1), p, (int) allocation, + nullptr, nullptr); + if(bytes != 0) + { + char *end = strchr(p, 0); + while(end[-1] == 10 || end[-1] == 13) + { + --end; + } + *end = 0; // NOLINT + _base::atomic_refcounted_string_ref ret(p, end - p); + free(p); + return ret; + } + free(p); // NOLINT + if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/) + { + allocation += allocation >> 2; + continue; + } + errcode = EILSEQ; + return _base::string_ref("failed to get message from system"); + } +#else + auto wlen = static_cast(strlen(ce.ErrorMessage())); + auto *p = static_cast(malloc(wlen + 1)); // NOLINT + if(p == nullptr) + { + errcode = ENOMEM; + return _base::string_ref("failed to get message from system"); + } + memcpy(p, ce.ErrorMessage(), wlen + 1); + char *end = strchr(p, 0); + while(end[-1] == 10 || end[-1] == 13) + { + --end; + } + *end = 0; // NOLINT + _base::atomic_refcounted_string_ref ret(p, end - p); + free(p); + return ret; +#endif + } + +public: + //! The value type of the COM code, which is a `HRESULT` + using value_type = HRESULT; + using _base::string_ref; + +public: + //! Default constructor + constexpr explicit _com_code_domain(typename _base::unique_id_type id = 0xdc8275428b4effac) noexcept + : _base(id) + { + } + _com_code_domain(const _com_code_domain &) = default; + _com_code_domain(_com_code_domain &&) = default; + _com_code_domain &operator=(const _com_code_domain &) = default; + _com_code_domain &operator=(_com_code_domain &&) = default; + ~_com_code_domain() = default; + + //! Constexpr singleton getter. Returns the constexpr com_code_domain variable. + static inline constexpr const _com_code_domain &get(); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref("COM domain"); + return 0; + } // NOLINT + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); + return static_cast(code).value() < 0; // NOLINT + } + /*! Note semantic equivalence testing is only implemented for `FACILITY_WIN32` and `FACILITY_NT_BIT`. + */ + SYSTEM_ERROR2_CONSTEXPR20 virtual bool + _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); + const auto &c1 = static_cast(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + return c1.value() == c2.value(); + } + if((c1.value() & FACILITY_NT_BIT) != 0) + { + if(code2.domain() == nt_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(c2.value() == (c1.value() & ~FACILITY_NT_BIT)) + { + return true; + } + } + else if(code2.domain() == generic_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(static_cast(c2.value()) == _nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT)) + { + return true; + } + } + } + else if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32) + { + if(code2.domain() == win32_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(c2.value() == HRESULT_CODE(c1.value())) + { + return true; + } + } + else if(code2.domain() == generic_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(static_cast(c2.value()) == _win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value()))) + { + return true; + } + } + } + return false; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override + { + assert(args.code.domain() == *this); + const auto &c1 = static_cast(args.code); // NOLINT + if(c1.value() == S_OK) + { + args.ret = generic_code(errc::success); + return; + } + if((c1.value() & FACILITY_NT_BIT) != 0) + { + args.ret = generic_code(static_cast(_nt_code_domain::_nt_code_to_errno(c1.value() & ~FACILITY_NT_BIT))); + return; + } + if(HRESULT_FACILITY(c1.value()) == FACILITY_WIN32) + { + args.ret = generic_code(static_cast(_win32_code_domain::_win32_code_to_errno(HRESULT_CODE(c1.value())))); + return; + } + args.ret = generic_code(errc::unknown); + } + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + int ret = 0; + args.ret = _make_string_ref(ret, c.value()); + return ret; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + throw status_error<_com_code_domain>(c); + } +#endif +}; +//! (Windows only) A constexpr source variable for the COM code domain. Returned by `_com_code_domain::get()`. +constexpr _com_code_domain com_code_domain; +inline constexpr const _com_code_domain &_com_code_domain::get() +{ + return com_code_domain; +} + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/config.hpp b/include/status-code/config.hpp new file mode 100644 index 000000000..662070b22 --- /dev/null +++ b/include/status-code/config.hpp @@ -0,0 +1,523 @@ +/* Proposed SG14 status_code +(C) 2018 - 2026 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_CONFIG_HPP +#define SYSTEM_ERROR2_CONFIG_HPP + +// < 0.1 each +#include +#include // for size_t +#include // for free + +// 0.22 +#include + +// 0.29 +#include + +// 0.28 (0.15 of which is exception_ptr) +#include // for std::exception +// includes , includes +#include + +// 0.01 +#include + +#ifndef SYSTEM_ERROR2_HAVE_BIT_CAST +#ifdef __has_include +#if __has_include() && (__cplusplus >= 202002L || _HAS_CXX20) +#define SYSTEM_ERROR2_HAVE_BIT_CAST 1 +#endif +#elif __cplusplus >= 202002L +#define SYSTEM_ERROR2_HAVE_BIT_CAST 1 +#endif +#ifndef SYSTEM_ERROR2_HAVE_BIT_CAST +#define SYSTEM_ERROR2_HAVE_BIT_CAST 0 +#endif +#endif +#if SYSTEM_ERROR2_HAVE_BIT_CAST +#include +#if __cpp_lib_bit_cast < 201806L +#undef SYSTEM_ERROR2_HAVE_BIT_CAST +#define SYSTEM_ERROR2_HAVE_BIT_CAST 0 +#endif +#endif + +#if SYSTEM_ERROR2_USE_STD_ADDRESSOF +#include // for std::addressof +#define SYSTEM_ERROR2_ADDRESS_OF(...) std::addressof(__VA_ARGS__) +#else +#define SYSTEM_ERROR2_ADDRESS_OF(...) (&__VA_ARGS__) +#endif + +#ifndef SYSTEM_ERROR2_CONSTEXPR14 +#if defined(STANDARDESE_IS_IN_THE_HOUSE) || __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */ +//! Defined to be `constexpr` when on C++ 14 or better compilers. Usually automatic, can be overriden. +#define SYSTEM_ERROR2_CONSTEXPR14 constexpr +#else +#define SYSTEM_ERROR2_CONSTEXPR14 +#endif +#endif + +#ifndef SYSTEM_ERROR2_CONSTEXPR20 +#if defined(STANDARDESE_IS_IN_THE_HOUSE) || __cplusplus >= 202000 || _HAS_CXX20 +//! Defined to be `constexpr` when on C++ 20 or better compilers. Usually automatic, can be overriden. +#define SYSTEM_ERROR2_CONSTEXPR20 constexpr +#else +#define SYSTEM_ERROR2_CONSTEXPR20 +#endif +#endif + + +#ifndef SYSTEM_ERROR2_NORETURN +#if defined(STANDARDESE_IS_IN_THE_HOUSE) || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */) +#define SYSTEM_ERROR2_NORETURN [[noreturn]] +#endif +#endif +#if !defined(SYSTEM_ERROR2_NORETURN) +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(noreturn) +#define SYSTEM_ERROR2_NORETURN [[noreturn]] +#endif +#endif +#endif +#if !defined(SYSTEM_ERROR2_NORETURN) +#if defined(_MSC_VER) +#define SYSTEM_ERROR2_NORETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define SYSTEM_ERROR2_NORETURN __attribute__((__noreturn__)) +#else +#define SYSTEM_ERROR2_NORETURN +#endif +#endif +// GCCs before 7 don't grok [[noreturn]] virtual functions, and warn annoyingly +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 7 +#undef SYSTEM_ERROR2_NORETURN +#define SYSTEM_ERROR2_NORETURN +#endif + +#ifndef SYSTEM_ERROR2_NODISCARD +#if defined(STANDARDESE_IS_IN_THE_HOUSE) || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */) +#define SYSTEM_ERROR2_NODISCARD [[nodiscard]] +#endif +#endif +#ifndef SYSTEM_ERROR2_NODISCARD +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(nodiscard) +#define SYSTEM_ERROR2_NODISCARD [[nodiscard]] +#endif +#elif defined(__clang__) +#define SYSTEM_ERROR2_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +// _Must_inspect_result_ expands into this +#define SYSTEM_ERROR2_NODISCARD \ + __declspec("SAL_name" \ + "(" \ + "\"_Must_inspect_result_\"" \ + "," \ + "\"\"" \ + "," \ + "\"2\"" \ + ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") \ + __declspec("SAL_checkReturn") __declspec("SAL_end") +#endif +#endif +#ifndef SYSTEM_ERROR2_NODISCARD +#define SYSTEM_ERROR2_NODISCARD +#endif + +#ifndef SYSTEM_ERROR2_TRIVIAL_ABI +#if defined(STANDARDESE_IS_IN_THE_HOUSE) || (__clang_major__ >= 7 && !defined(__APPLE__)) +//! Defined to be `[[clang::trivial_abi]]` when on a new enough clang compiler. Usually automatic, can be overriden. +#define SYSTEM_ERROR2_TRIVIAL_ABI [[clang::trivial_abi]] +#else +#define SYSTEM_ERROR2_TRIVIAL_ABI +#endif +#endif + +#if defined(__cpp_concepts) && !defined(SYSTEM_ERROR2_DISABLE_CONCEPTS_SUPPORT) +#define SYSTEM_ERROR2_GLUE(x, y) x y + +#define SYSTEM_ERROR2_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define SYSTEM_ERROR2_EXPAND_ARGS(args) SYSTEM_ERROR2_RETURN_ARG_COUNT args +#define SYSTEM_ERROR2_COUNT_ARGS_MAX8(...) SYSTEM_ERROR2_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) + +#define SYSTEM_ERROR2_OVERLOAD_MACRO2(name, count) name##count +#define SYSTEM_ERROR2_OVERLOAD_MACRO1(name, count) SYSTEM_ERROR2_OVERLOAD_MACRO2(name, count) +#define SYSTEM_ERROR2_OVERLOAD_MACRO(name, count) SYSTEM_ERROR2_OVERLOAD_MACRO1(name, count) + +#define SYSTEM_ERROR2_CALL_OVERLOAD(name, ...) \ + SYSTEM_ERROR2_GLUE(SYSTEM_ERROR2_OVERLOAD_MACRO(name, SYSTEM_ERROR2_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) + +#define SYSTEM_ERROR2_TREQUIRES_EXPAND8(a, b, c, d, e, f, g, h) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND7(b, c, d, e, f, g, h) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND7(a, b, c, d, e, f, g) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND6(b, c, d, e, f, g) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND6(a, b, c, d, e, f) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND5(b, c, d, e, f) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND5(a, b, c, d, e) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND4(b, c, d, e) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND4(a, b, c, d) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND3(b, c, d) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND3(a, b, c) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND2(b, c) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND2(a, b) a &&SYSTEM_ERROR2_TREQUIRES_EXPAND1(b) +#define SYSTEM_ERROR2_TREQUIRES_EXPAND1(a) a + +//! Expands into a && b && c && ... +#define SYSTEM_ERROR2_TREQUIRES(...) requires SYSTEM_ERROR2_CALL_OVERLOAD(SYSTEM_ERROR2_TREQUIRES_EXPAND, __VA_ARGS__) + +#define SYSTEM_ERROR2_TEMPLATE(...) template <__VA_ARGS__> +#define SYSTEM_ERROR2_TEXPR(...) requires { (__VA_ARGS__); } +#define SYSTEM_ERROR2_TPRED(...) (__VA_ARGS__) +#if !defined(_MSC_VER) || _MSC_FULL_VER >= 192400000 // VS 2019 16.3 is broken here +#define SYSTEM_ERROR2_REQUIRES(...) requires(__VA_ARGS__) +#else +#define SYSTEM_ERROR2_REQUIRES(...) +#endif +#else +#define SYSTEM_ERROR2_TEMPLATE(...) template <__VA_ARGS__ +#define SYSTEM_ERROR2_TREQUIRES(...) , __VA_ARGS__ > +#define SYSTEM_ERROR2_TEXPR(...) typename = decltype(__VA_ARGS__) +#ifdef _MSC_VER +// MSVC gives an error if every specialisation of a template is always ill-formed, so +// the more powerful SFINAE form below causes pukeage :( +#define SYSTEM_ERROR2_TPRED(...) typename = typename std::enable_if<(__VA_ARGS__)>::type +#else +#define SYSTEM_ERROR2_TPRED(...) typename std::enable_if<(__VA_ARGS__), bool>::type = true +#endif +#define SYSTEM_ERROR2_REQUIRES(...) +#endif + +#ifndef SYSTEM_ERROR2_NAMESPACE +//! The system_error2 namespace name. +#define SYSTEM_ERROR2_NAMESPACE system_error2 +//! Begins the system_error2 namespace. +#define SYSTEM_ERROR2_NAMESPACE_BEGIN \ + namespace system_error2 \ + { +//! Ends the system_error2 namespace. +#define SYSTEM_ERROR2_NAMESPACE_END } +#endif + +//! Namespace for the library +SYSTEM_ERROR2_NAMESPACE_BEGIN + +//! Namespace for user specialised traits +namespace traits +{ + /*! Specialise to true if you guarantee that a type is move bitcopying (i.e. + its move constructor equals copying bits from old to new, old is left in a + default constructed state, and calling the destructor on a default constructed + instance is trivial). All trivially copyable types are move bitcopying by + definition, and that is the unspecialised implementation. + */ + template struct is_move_bitcopying + { + static constexpr bool value = std::is_trivially_copyable::value; + }; +} // namespace traits + +namespace detail +{ +#if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */ + inline constexpr size_t cstrlen(const char *str) + { + const char *end = nullptr; + for(end = str; *end != 0; ++end) // NOLINT + ; + return end - str; + } +#else + inline constexpr size_t cstrlen_(const char *str, size_t acc) + { + return (str[0] == 0) ? acc : cstrlen_(str + 1, acc + 1); + } + inline constexpr size_t cstrlen(const char *str) + { + return cstrlen_(str, 0); + } +#endif + +#if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __cpp_lib_remove_cvref >= 201711L + + template using remove_cvref = std::remove_cvref; + +#else + + template struct remove_cvref + { + using type = typename std::remove_cv::type>::type; + }; + +#endif + + /* A partially compliant implementation of C++20's std::bit_cast function contributed + by Jesse Towner. + + Our bit_cast is only guaranteed to be constexpr when both the input and output + arguments are either integrals or enums. However, this covers most use cases + since the vast majority of status_codes have an underlying type that is either + an integral or enum. We still attempt a constexpr union-based type pun for non-array + input types, which some compilers accept. For array inputs, we fall back to + non-constexpr memmove. + */ + + template + using is_integral_or_enum = std::integral_constant::value || std::is_enum::value>; + + template + using is_static_castable = + std::integral_constant::value && is_integral_or_enum::value>; + + template + using is_union_castable = std::integral_constant::value && + !std::is_array::value && !std::is_array::value>; + + template + using is_bit_castable = + std::integral_constant::value && + traits::is_move_bitcopying::value>; + + template union bit_cast_union + { + From source; + To target; + }; + +#if SYSTEM_ERROR2_HAVE_BIT_CAST + using std::bit_cast; // available for all trivially copyable types + + // For move bit copying types + template + requires(is_bit_castable::value // + && is_union_castable::value // + && (!std::is_trivially_copyable_v // + || !std::is_trivially_copyable_v) ) // + constexpr To bit_cast(const From &from) noexcept + { + return bit_cast_union{from}.target; + } + template + requires(is_bit_castable::value // + && !is_union_castable::value // + && (!std::is_trivially_copyable_v // + || !std::is_trivially_copyable_v) ) // + To bit_cast(const From &from) noexcept + { + bit_cast_union ret; + memmove(&ret.source, &from, sizeof(ret.source)); + return ret.target; + } +#else + SYSTEM_ERROR2_TEMPLATE(class To, class From, int = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_bit_castable::value // + &&is_static_castable::value // + && !is_union_castable::value)) + constexpr To bit_cast(const From &from) noexcept + { + return static_cast(from); + } + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + SYSTEM_ERROR2_TEMPLATE(class To, class From, long = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_bit_castable::value // + && !is_static_castable::value // + && is_union_castable::value)) + constexpr To bit_cast(const From &from) noexcept + { + return bit_cast_union{from}.target; + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_bit_castable::value // + && !is_static_castable::value // + && !is_union_castable::value)) + To bit_cast(const From &from) noexcept + { + bit_cast_union ret; + memmove(&ret.source, &from, sizeof(ret.source)); + return ret.target; + } +#endif + + /* erasure_cast performs a bit_cast with additional rules to handle types + of differing sizes. For integral & enum types, it may perform a narrowing + or widing conversion with static_cast if necessary, before doing the final + conversion with bit_cast. When casting to or from non-integral, non-enum + types it may insert the value into another object with extra padding bytes + to satisfy bit_cast's preconditions that both types have the same size. */ + + template + using is_erasure_castable = + std::integral_constant::value && traits::is_move_bitcopying::value>; + + template ::value> struct identity_or_underlying_type + { + using type = T; + }; + template struct identity_or_underlying_type + { + using type = typename std::underlying_type::type; + }; + + template + using erasure_integer_type = typename std::conditional< + std::is_signed::type>::value, + typename std::make_signed::type>::type, + typename std::make_unsigned::type>::type>::type; + + template struct padded_erasure_object + { + static_assert(traits::is_move_bitcopying::value, + "ErasedType must be TriviallyCopyable or MoveBitcopying"); + static_assert(alignof(ErasedType) <= sizeof(ErasedType), "ErasedType must not be over-aligned"); + ErasedType value; + char padding[N]; + constexpr explicit padded_erasure_object(const ErasedType &v) noexcept + : value(v) + , padding{} + { + } + }; + + SYSTEM_ERROR2_TEMPLATE(class To, class From) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) == sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return bit_cast(from); + } + +#if defined(_WIN32) || defined(__APPLE__) || __LITTLE_ENDIAN__ || \ +(defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) + // We can avoid the type pun on little endian architectures which can aid optimisation + SYSTEM_ERROR2_TEMPLATE(class To, class From, long = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( + is_erasure_castable::value &&is_static_castable::value && (sizeof(To) < sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return static_cast(bit_cast>(from)); + } + + SYSTEM_ERROR2_TEMPLATE(class To, class From, int = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( + is_erasure_castable::value &&is_static_castable::value && (sizeof(To) > sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return bit_cast(static_cast>(from)); + } + + SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_erasure_castable::value && + !is_static_castable::value && (sizeof(To) < sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return bit_cast>(from).value; + } + + SYSTEM_ERROR2_TEMPLATE(class To, class From, char = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_erasure_castable::value && + !is_static_castable::value && (sizeof(To) > sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return bit_cast(padded_erasure_object{from}); + } +#else + SYSTEM_ERROR2_TEMPLATE(class To, class From, short = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) < sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return bit_cast>(from).value; + } + + SYSTEM_ERROR2_TEMPLATE(class To, class From, char = 5) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_erasure_castable::value && (sizeof(To) > sizeof(From)))) + constexpr To erasure_cast(const From &from) noexcept + { + return bit_cast(padded_erasure_object{from}); + } +#endif +} // namespace detail +SYSTEM_ERROR2_NAMESPACE_END + +#ifndef SYSTEM_ERROR2_FATAL +#ifdef SYSTEM_ERROR2_NOT_POSIX +#if !defined(_MSC_VER) || defined(__clang__) +#warning "If SYSTEM_ERROR2_NOT_POSIX is defined, you should define your own SYSTEM_ERROR2_FATAL implementation!" +#endif +#include // for abort +#define SYSTEM_ERROR2_FATAL(msg) abort() +#else +#include // for abort +#ifndef SYSTEM_ERROR2_AVOID_STDIO_INCLUDE +#ifdef __APPLE__ +#define SYSTEM_ERROR2_AVOID_STDIO_INCLUDE 0 +#else +#define SYSTEM_ERROR2_AVOID_STDIO_INCLUDE 1 +#endif +#endif +#if !SYSTEM_ERROR2_AVOID_STDIO_INCLUDE +#if defined(_MSC_VER) +#include // for write +#else +#include // for write +#endif +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN +namespace detail +{ + namespace avoid_stdio_include + { +#if SYSTEM_ERROR2_AVOID_STDIO_INCLUDE +#if !defined(__APPLE__) && !defined(_MSC_VER) + extern "C" ptrdiff_t write(int, const void *, size_t); +#elif defined(_MSC_VER) + extern ptrdiff_t write(int, const void *, size_t); +#if (defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) || \ +(defined(__arm__) || defined(_M_ARM)) +#pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YA_JHPEBX_K@Z=write") +#elif defined(__x86__) || defined(_M_IX86) || defined(__i386__) +#pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YAHHPBXI@Z=_write") +#else +#error Unknown architecture +#endif +#endif +#endif + } // namespace avoid_stdio_include + inline void do_fatal_exit(const char *msg) + { + using namespace avoid_stdio_include; + write(2 /*stderr*/, msg, cstrlen(msg)); + write(2 /*stderr*/, "\n", 1); + abort(); + } +} // namespace detail +SYSTEM_ERROR2_NAMESPACE_END +//! Prints msg to stderr, and calls `std::terminate()`. Can be overriden via predefinition. +#define SYSTEM_ERROR2_FATAL(msg) ::SYSTEM_ERROR2_NAMESPACE::detail::do_fatal_exit(msg) +#endif +#endif + +#endif diff --git a/include/status-code/detail/nt_code_to_generic_code.ipp b/include/status-code/detail/nt_code_to_generic_code.ipp new file mode 100644 index 000000000..c5aaed100 --- /dev/null +++ b/include/status-code/detail/nt_code_to_generic_code.ipp @@ -0,0 +1,98 @@ +case 0x80000002: return EACCES; +case 0x8000000f: return EAGAIN; +case 0x80000010: return EAGAIN; +case 0x80000011: return EBUSY; +case 0xc0000002: return ENOSYS; +case 0xc0000005: return EACCES; +case 0xc0000008: return EINVAL; +case 0xc000000e: return ENOENT; +case 0xc000000f: return ENOENT; +case 0xc0000010: return ENOSYS; +case 0xc0000013: return EAGAIN; +case 0xc0000017: return ENOMEM; +case 0xc000001c: return ENOSYS; +case 0xc000001e: return EACCES; +case 0xc000001f: return EACCES; +case 0xc0000021: return EACCES; +case 0xc0000022: return EACCES; +case 0xc0000024: return EINVAL; +case 0xc0000033: return EINVAL; +case 0xc0000034: return ENOENT; +case 0xc0000035: return EEXIST; +case 0xc0000037: return EINVAL; +case 0xc000003a: return ENOENT; +case 0xc0000040: return ENOMEM; +case 0xc0000041: return EACCES; +case 0xc0000042: return EINVAL; +case 0xc0000043: return EACCES; +case 0xc000004b: return EACCES; +case 0xc0000054: return ENOLCK; +case 0xc0000055: return ENOLCK; +case 0xc0000056: return EACCES; +case 0xc000007f: return ENOSPC; +case 0xc0000087: return ENOMEM; +case 0xc0000097: return ENOMEM; +case 0xc000009b: return ENOENT; +case 0xc000009e: return EAGAIN; +case 0xc00000a2: return EACCES; +case 0xc00000a3: return EAGAIN; +case 0xc00000af: return ENOSYS; +case 0xc00000ba: return EACCES; +case 0xc00000c0: return ENODEV; +case 0xc00000d4: return EXDEV; +case 0xc00000d5: return EACCES; +case 0xc00000fb: return ENOENT; +case 0xc0000101: return ENOTEMPTY; +case 0xc0000103: return EINVAL; +case 0xc0000107: return EBUSY; +case 0xc0000108: return EBUSY; +case 0xc000010a: return EACCES; +case 0xc000011f: return EMFILE; +case 0xc0000120: return ECANCELED; +case 0xc0000121: return EACCES; +case 0xc0000123: return EACCES; +case 0xc0000128: return EINVAL; +case 0xc0000189: return EACCES; +case 0xc00001ad: return ENOMEM; +case 0xc000022d: return EAGAIN; +case 0xc0000235: return EINVAL; +case 0xc000026e: return EAGAIN; +case 0xc000028a: return EACCES; +case 0xc000028b: return EACCES; +case 0xc000028d: return EACCES; +case 0xc000028e: return EACCES; +case 0xc000028f: return EACCES; +case 0xc0000290: return EACCES; +case 0xc000029c: return ENOSYS; +case 0xc00002c5: return EACCES; +case 0xc00002d3: return EAGAIN; +case 0xc00002ea: return EACCES; +case 0xc00002f0: return ENOENT; +case 0xc0000373: return ENOMEM; +case 0xc0000416: return ENOMEM; +case 0xc0000433: return EBUSY; +case 0xc0000434: return EBUSY; +case 0xc0000455: return EINVAL; +case 0xc0000467: return EACCES; +case 0xc0000491: return ENOENT; +case 0xc0000495: return EAGAIN; +case 0xc0000503: return EAGAIN; +case 0xc0000507: return EBUSY; +case 0xc0000512: return EACCES; +case 0xc000070a: return EINVAL; +case 0xc000070b: return EINVAL; +case 0xc000070c: return EINVAL; +case 0xc000070d: return EINVAL; +case 0xc000070e: return EINVAL; +case 0xc000070f: return EINVAL; +case 0xc0000710: return ENOSYS; +case 0xc0000711: return ENOSYS; +case 0xc0000716: return EINVAL; +case 0xc000071b: return ENOSYS; +case 0xc000071d: return ENOSYS; +case 0xc000071e: return ENOSYS; +case 0xc000071f: return ENOSYS; +case 0xc0000720: return ENOSYS; +case 0xc0000721: return ENOSYS; +case 0xc000080f: return EAGAIN; +case 0xc000a203: return EACCES; diff --git a/include/status-code/detail/nt_code_to_win32_code.ipp b/include/status-code/detail/nt_code_to_win32_code.ipp new file mode 100644 index 000000000..cd85827a0 --- /dev/null +++ b/include/status-code/detail/nt_code_to_win32_code.ipp @@ -0,0 +1,1018 @@ +case 0x80000002: return 0x3e6; +case 0x80000005: return 0xea; +case 0x80000006: return 0x12; +case 0x80000007: return 0x2a3; +case 0x8000000a: return 0x2a4; +case 0x8000000b: return 0x56f; +case 0x8000000c: return 0x2a8; +case 0x8000000d: return 0x12b; +case 0x8000000e: return 0x1c; +case 0x8000000f: return 0x15; +case 0x80000010: return 0x15; +case 0x80000011: return 0xaa; +case 0x80000012: return 0x103; +case 0x80000013: return 0xfe; +case 0x80000014: return 0xff; +case 0x80000015: return 0xff; +case 0x80000016: return 0x456; +case 0x80000017: return 0x2a5; +case 0x80000018: return 0x2a6; +case 0x8000001a: return 0x103; +case 0x8000001b: return 0x44d; +case 0x8000001c: return 0x456; +case 0x8000001d: return 0x457; +case 0x8000001e: return 0x44c; +case 0x8000001f: return 0x44e; +case 0x80000020: return 0x2a7; +case 0x80000021: return 0x44f; +case 0x80000022: return 0x450; +case 0x80000023: return 0x702; +case 0x80000024: return 0x713; +case 0x80000025: return 0x962; +case 0x80000026: return 0x2aa; +case 0x80000027: return 0x10f4; +case 0x80000028: return 0x2ab; +case 0x80000029: return 0x2ac; +case 0x8000002a: return 0x2ad; +case 0x8000002b: return 0x2ae; +case 0x8000002c: return 0x2af; +case 0x8000002d: return 0x2a9; +case 0x8000002e: return 0x321; +case 0x8000002f: return 0x324; +case 0x80000030: return 0xab; +case 0x80000032: return 0xeb; +case 0x80000288: return 0x48d; +case 0x80000289: return 0x48e; +case 0x80000803: return 0x1abb; +case 0x8000a127: return 0x3bdf; +case 0x8000cf00: return 0x16e; +case 0x8000cf04: return 0x16d; +case 0x8000cf05: return 0x176; +case 0x80130001: return 0x13c5; +case 0x80130002: return 0x13c6; +case 0x80130003: return 0x13c7; +case 0x80130004: return 0x13c8; +case 0x80130005: return 0x13c9; +case 0x80190009: return 0x19e5; +case 0x80190029: return 0x1aa0; +case 0x80190031: return 0x1aa2; +case 0x80190041: return 0x1ab3; +case 0x80190042: return 0x1ab4; +case 0x801c0001: return 0x7a; +case 0xc0000001: return 0x1f; +case 0xc0000002: return 0x1; +case 0xc0000003: return 0x57; +case 0xc0000004: return 0x18; +case 0xc0000005: return 0x3e6; +case 0xc0000006: return 0x3e7; +case 0xc0000007: return 0x5ae; +case 0xc0000008: return 0x6; +case 0xc0000009: return 0x3e9; +case 0xc000000a: return 0xc1; +case 0xc000000b: return 0x57; +case 0xc000000c: return 0x21d; +case 0xc000000d: return 0x57; +case 0xc000000e: return 0x2; +case 0xc000000f: return 0x2; +case 0xc0000010: return 0x1; +case 0xc0000011: return 0x26; +case 0xc0000012: return 0x22; +case 0xc0000013: return 0x15; +case 0xc0000014: return 0x6f9; +case 0xc0000015: return 0x1b; +case 0xc0000016: return 0xea; +case 0xc0000017: return 0x8; +case 0xc0000018: return 0x1e7; +case 0xc0000019: return 0x1e7; +case 0xc000001a: return 0x57; +case 0xc000001b: return 0x57; +case 0xc000001c: return 0x1; +case 0xc000001e: return 0x5; +case 0xc000001f: return 0x5; +case 0xc0000020: return 0xc1; +case 0xc0000021: return 0x5; +case 0xc0000022: return 0x5; +case 0xc0000023: return 0x7a; +case 0xc0000024: return 0x6; +case 0xc0000027: return 0x21e; +case 0xc0000028: return 0x21f; +case 0xc0000029: return 0x220; +case 0xc000002a: return 0x9e; +case 0xc000002c: return 0x1e7; +case 0xc000002d: return 0x1e7; +case 0xc000002e: return 0x221; +case 0xc000002f: return 0x222; +case 0xc0000030: return 0x57; +case 0xc0000031: return 0x223; +case 0xc0000032: return 0x571; +case 0xc0000033: return 0x7b; +case 0xc0000034: return 0x2; +case 0xc0000035: return 0xb7; +case 0xc0000036: return 0x72a; +case 0xc0000037: return 0x6; +case 0xc0000038: return 0x224; +case 0xc0000039: return 0xa1; +case 0xc000003a: return 0x3; +case 0xc000003b: return 0xa1; +case 0xc000003c: return 0x45d; +case 0xc000003d: return 0x45d; +case 0xc000003e: return 0x17; +case 0xc000003f: return 0x17; +case 0xc0000040: return 0x8; +case 0xc0000041: return 0x5; +case 0xc0000042: return 0x6; +case 0xc0000043: return 0x20; +case 0xc0000044: return 0x718; +case 0xc0000045: return 0x57; +case 0xc0000046: return 0x120; +case 0xc0000047: return 0x12a; +case 0xc0000048: return 0x57; +case 0xc0000049: return 0x57; +case 0xc000004a: return 0x9c; +case 0xc000004b: return 0x5; +case 0xc000004c: return 0x57; +case 0xc000004d: return 0x57; +case 0xc000004e: return 0x57; +case 0xc000004f: return 0x11a; +case 0xc0000050: return 0xff; +case 0xc0000051: return 0x570; +case 0xc0000052: return 0x570; +case 0xc0000053: return 0x570; +case 0xc0000054: return 0x21; +case 0xc0000055: return 0x21; +case 0xc0000056: return 0x5; +case 0xc0000057: return 0x32; +case 0xc0000058: return 0x519; +case 0xc0000059: return 0x51a; +case 0xc000005a: return 0x51b; +case 0xc000005b: return 0x51c; +case 0xc000005c: return 0x51d; +case 0xc000005d: return 0x51e; +case 0xc000005e: return 0x51f; +case 0xc000005f: return 0x520; +case 0xc0000060: return 0x521; +case 0xc0000061: return 0x522; +case 0xc0000062: return 0x523; +case 0xc0000063: return 0x524; +case 0xc0000064: return 0x525; +case 0xc0000065: return 0x526; +case 0xc0000066: return 0x527; +case 0xc0000067: return 0x528; +case 0xc0000068: return 0x529; +case 0xc0000069: return 0x52a; +case 0xc000006a: return 0x56; +case 0xc000006b: return 0x52c; +case 0xc000006c: return 0x52d; +case 0xc000006d: return 0x52e; +case 0xc000006e: return 0x52f; +case 0xc000006f: return 0x530; +case 0xc0000070: return 0x531; +case 0xc0000071: return 0x532; +case 0xc0000072: return 0x533; +case 0xc0000073: return 0x534; +case 0xc0000074: return 0x535; +case 0xc0000075: return 0x536; +case 0xc0000076: return 0x537; +case 0xc0000077: return 0x538; +case 0xc0000078: return 0x539; +case 0xc0000079: return 0x53a; +case 0xc000007a: return 0x7f; +case 0xc000007b: return 0xc1; +case 0xc000007c: return 0x3f0; +case 0xc000007d: return 0x53c; +case 0xc000007e: return 0x9e; +case 0xc000007f: return 0x70; +case 0xc0000080: return 0x53d; +case 0xc0000081: return 0x53e; +case 0xc0000082: return 0x44; +case 0xc0000083: return 0x103; +case 0xc0000084: return 0x53f; +case 0xc0000085: return 0x103; +case 0xc0000086: return 0x9a; +case 0xc0000087: return 0xe; +case 0xc0000088: return 0x1e7; +case 0xc0000089: return 0x714; +case 0xc000008a: return 0x715; +case 0xc000008b: return 0x716; +case 0xc0000095: return 0x216; +case 0xc0000097: return 0x8; +case 0xc0000098: return 0x3ee; +case 0xc0000099: return 0x540; +case 0xc000009a: return 0x5aa; +case 0xc000009b: return 0x3; +case 0xc000009c: return 0x17; +case 0xc000009d: return 0x48f; +case 0xc000009e: return 0x15; +case 0xc000009f: return 0x1e7; +case 0xc00000a0: return 0x1e7; +case 0xc00000a1: return 0x5ad; +case 0xc00000a2: return 0x13; +case 0xc00000a3: return 0x15; +case 0xc00000a4: return 0x541; +case 0xc00000a5: return 0x542; +case 0xc00000a6: return 0x543; +case 0xc00000a7: return 0x544; +case 0xc00000a8: return 0x545; +case 0xc00000a9: return 0x57; +case 0xc00000aa: return 0x225; +case 0xc00000ab: return 0xe7; +case 0xc00000ac: return 0xe7; +case 0xc00000ad: return 0xe6; +case 0xc00000ae: return 0xe7; +case 0xc00000af: return 0x1; +case 0xc00000b0: return 0xe9; +case 0xc00000b1: return 0xe8; +case 0xc00000b2: return 0x217; +case 0xc00000b3: return 0x218; +case 0xc00000b4: return 0xe6; +case 0xc00000b5: return 0x79; +case 0xc00000b6: return 0x26; +case 0xc00000b7: return 0x226; +case 0xc00000b8: return 0x227; +case 0xc00000b9: return 0x228; +case 0xc00000ba: return 0x5; +case 0xc00000bb: return 0x32; +case 0xc00000bc: return 0x33; +case 0xc00000bd: return 0x34; +case 0xc00000be: return 0x35; +case 0xc00000bf: return 0x36; +case 0xc00000c0: return 0x37; +case 0xc00000c1: return 0x38; +case 0xc00000c2: return 0x39; +case 0xc00000c3: return 0x3a; +case 0xc00000c4: return 0x3b; +case 0xc00000c5: return 0x3c; +case 0xc00000c6: return 0x3d; +case 0xc00000c7: return 0x3e; +case 0xc00000c8: return 0x3f; +case 0xc00000c9: return 0x40; +case 0xc00000ca: return 0x41; +case 0xc00000cb: return 0x42; +case 0xc00000cc: return 0x43; +case 0xc00000cd: return 0x44; +case 0xc00000ce: return 0x45; +case 0xc00000cf: return 0x46; +case 0xc00000d0: return 0x47; +case 0xc00000d1: return 0x48; +case 0xc00000d2: return 0x58; +case 0xc00000d3: return 0x229; +case 0xc00000d4: return 0x11; +case 0xc00000d5: return 0x5; +case 0xc00000d6: return 0xf0; +case 0xc00000d7: return 0x546; +case 0xc00000d8: return 0x22a; +case 0xc00000d9: return 0xe8; +case 0xc00000da: return 0x547; +case 0xc00000db: return 0x22b; +case 0xc00000dc: return 0x548; +case 0xc00000dd: return 0x549; +case 0xc00000de: return 0x54a; +case 0xc00000df: return 0x54b; +case 0xc00000e0: return 0x54c; +case 0xc00000e1: return 0x54d; +case 0xc00000e2: return 0x12c; +case 0xc00000e3: return 0x12d; +case 0xc00000e4: return 0x54e; +case 0xc00000e5: return 0x54f; +case 0xc00000e6: return 0x550; +case 0xc00000e7: return 0x551; +case 0xc00000e8: return 0x6f8; +case 0xc00000e9: return 0x45d; +case 0xc00000ea: return 0x22c; +case 0xc00000eb: return 0x22d; +case 0xc00000ec: return 0x22e; +case 0xc00000ed: return 0x552; +case 0xc00000ee: return 0x553; +case 0xc00000ef: return 0x57; +case 0xc00000f0: return 0x57; +case 0xc00000f1: return 0x57; +case 0xc00000f2: return 0x57; +case 0xc00000f3: return 0x57; +case 0xc00000f4: return 0x57; +case 0xc00000f5: return 0x57; +case 0xc00000f6: return 0x57; +case 0xc00000f7: return 0x57; +case 0xc00000f8: return 0x57; +case 0xc00000f9: return 0x57; +case 0xc00000fa: return 0x57; +case 0xc00000fb: return 0x3; +case 0xc00000fc: return 0x420; +case 0xc00000fd: return 0x3e9; +case 0xc00000fe: return 0x554; +case 0xc00000ff: return 0x22f; +case 0xc0000100: return 0xcb; +case 0xc0000101: return 0x91; +case 0xc0000102: return 0x570; +case 0xc0000103: return 0x10b; +case 0xc0000104: return 0x555; +case 0xc0000105: return 0x556; +case 0xc0000106: return 0xce; +case 0xc0000107: return 0x961; +case 0xc0000108: return 0x964; +case 0xc000010a: return 0x5; +case 0xc000010b: return 0x557; +case 0xc000010c: return 0x230; +case 0xc000010d: return 0x558; +case 0xc000010e: return 0x420; +case 0xc000010f: return 0x21a; +case 0xc0000110: return 0x21a; +case 0xc0000111: return 0x21a; +case 0xc0000112: return 0x21a; +case 0xc0000113: return 0x21a; +case 0xc0000114: return 0x21a; +case 0xc0000115: return 0x21a; +case 0xc0000116: return 0x21a; +case 0xc0000117: return 0x5a4; +case 0xc0000118: return 0x231; +case 0xc0000119: return 0x233; +case 0xc000011a: return 0x234; +case 0xc000011b: return 0xc1; +case 0xc000011c: return 0x559; +case 0xc000011d: return 0x55a; +case 0xc000011e: return 0x3ee; +case 0xc000011f: return 0x4; +case 0xc0000120: return 0x3e3; +case 0xc0000121: return 0x5; +case 0xc0000122: return 0x4ba; +case 0xc0000123: return 0x5; +case 0xc0000124: return 0x55b; +case 0xc0000125: return 0x55c; +case 0xc0000126: return 0x55d; +case 0xc0000127: return 0x55e; +case 0xc0000128: return 0x6; +case 0xc0000129: return 0x235; +case 0xc000012a: return 0x236; +case 0xc000012b: return 0x55f; +case 0xc000012c: return 0x237; +case 0xc000012d: return 0x5af; +case 0xc000012e: return 0xc1; +case 0xc000012f: return 0xc1; +case 0xc0000130: return 0xc1; +case 0xc0000131: return 0xc1; +case 0xc0000132: return 0x238; +case 0xc0000133: return 0x576; +case 0xc0000134: return 0x239; +case 0xc0000135: return 0x7e; +case 0xc0000136: return 0x23a; +case 0xc0000137: return 0x23b; +case 0xc0000138: return 0xb6; +case 0xc0000139: return 0x7f; +case 0xc000013a: return 0x23c; +case 0xc000013b: return 0x40; +case 0xc000013c: return 0x40; +case 0xc000013d: return 0x33; +case 0xc000013e: return 0x3b; +case 0xc000013f: return 0x3b; +case 0xc0000140: return 0x3b; +case 0xc0000141: return 0x3b; +case 0xc0000142: return 0x45a; +case 0xc0000143: return 0x23d; +case 0xc0000144: return 0x23e; +case 0xc0000145: return 0x23f; +case 0xc0000146: return 0x240; +case 0xc0000147: return 0x242; +case 0xc0000148: return 0x7c; +case 0xc0000149: return 0x56; +case 0xc000014a: return 0x243; +case 0xc000014b: return 0x6d; +case 0xc000014c: return 0x3f1; +case 0xc000014d: return 0x3f8; +case 0xc000014e: return 0x244; +case 0xc000014f: return 0x3ed; +case 0xc0000150: return 0x45e; +case 0xc0000151: return 0x560; +case 0xc0000152: return 0x561; +case 0xc0000153: return 0x562; +case 0xc0000154: return 0x563; +case 0xc0000155: return 0x564; +case 0xc0000156: return 0x565; +case 0xc0000157: return 0x566; +case 0xc0000158: return 0x567; +case 0xc0000159: return 0x3ef; +case 0xc000015a: return 0x568; +case 0xc000015b: return 0x569; +case 0xc000015c: return 0x3f9; +case 0xc000015d: return 0x56a; +case 0xc000015e: return 0x245; +case 0xc000015f: return 0x45d; +case 0xc0000160: return 0x4db; +case 0xc0000161: return 0x246; +case 0xc0000162: return 0x459; +case 0xc0000163: return 0x247; +case 0xc0000164: return 0x248; +case 0xc0000165: return 0x462; +case 0xc0000166: return 0x463; +case 0xc0000167: return 0x464; +case 0xc0000168: return 0x465; +case 0xc0000169: return 0x466; +case 0xc000016a: return 0x467; +case 0xc000016b: return 0x468; +case 0xc000016c: return 0x45f; +case 0xc000016d: return 0x45d; +case 0xc000016e: return 0x249; +case 0xc0000172: return 0x451; +case 0xc0000173: return 0x452; +case 0xc0000174: return 0x453; +case 0xc0000175: return 0x454; +case 0xc0000176: return 0x455; +case 0xc0000177: return 0x469; +case 0xc0000178: return 0x458; +case 0xc000017a: return 0x56b; +case 0xc000017b: return 0x56c; +case 0xc000017c: return 0x3fa; +case 0xc000017d: return 0x3fb; +case 0xc000017e: return 0x56d; +case 0xc000017f: return 0x56e; +case 0xc0000180: return 0x3fc; +case 0xc0000181: return 0x3fd; +case 0xc0000182: return 0x57; +case 0xc0000183: return 0x45d; +case 0xc0000184: return 0x16; +case 0xc0000185: return 0x45d; +case 0xc0000186: return 0x45d; +case 0xc0000187: return 0x24a; +case 0xc0000188: return 0x5de; +case 0xc0000189: return 0x13; +case 0xc000018a: return 0x6fa; +case 0xc000018b: return 0x6fb; +case 0xc000018c: return 0x6fc; +case 0xc000018d: return 0x6fd; +case 0xc000018e: return 0x5dc; +case 0xc000018f: return 0x5dd; +case 0xc0000190: return 0x6fe; +case 0xc0000191: return 0x24b; +case 0xc0000192: return 0x700; +case 0xc0000193: return 0x701; +case 0xc0000194: return 0x46b; +case 0xc0000195: return 0x4c3; +case 0xc0000196: return 0x4c4; +case 0xc0000197: return 0x5df; +case 0xc0000198: return 0x70f; +case 0xc0000199: return 0x710; +case 0xc000019a: return 0x711; +case 0xc000019b: return 0x712; +case 0xc000019c: return 0x24c; +case 0xc000019d: return 0x420; +case 0xc000019e: return 0x130; +case 0xc000019f: return 0x131; +case 0xc00001a0: return 0x132; +case 0xc00001a1: return 0x133; +case 0xc00001a2: return 0x325; +case 0xc00001a3: return 0x134; +case 0xc00001a4: return 0x135; +case 0xc00001a5: return 0x136; +case 0xc00001a6: return 0x137; +case 0xc00001a7: return 0x139; +case 0xc00001a8: return 0x1abb; +case 0xc00001a9: return 0x32; +case 0xc00001aa: return 0x3d54; +case 0xc00001ab: return 0x329; +case 0xc00001ac: return 0x678; +case 0xc00001ad: return 0x8; +case 0xc00001ae: return 0x2f7; +case 0xc00001af: return 0x32d; +case 0xc0000201: return 0x41; +case 0xc0000202: return 0x572; +case 0xc0000203: return 0x3b; +case 0xc0000204: return 0x717; +case 0xc0000205: return 0x46a; +case 0xc0000206: return 0x6f8; +case 0xc0000207: return 0x4be; +case 0xc0000208: return 0x4be; +case 0xc0000209: return 0x44; +case 0xc000020a: return 0x34; +case 0xc000020b: return 0x40; +case 0xc000020c: return 0x40; +case 0xc000020d: return 0x40; +case 0xc000020e: return 0x44; +case 0xc000020f: return 0x3b; +case 0xc0000210: return 0x3b; +case 0xc0000211: return 0x3b; +case 0xc0000212: return 0x3b; +case 0xc0000213: return 0x3b; +case 0xc0000214: return 0x3b; +case 0xc0000215: return 0x3b; +case 0xc0000216: return 0x32; +case 0xc0000217: return 0x32; +case 0xc0000218: return 0x24d; +case 0xc0000219: return 0x24e; +case 0xc000021a: return 0x24f; +case 0xc000021b: return 0x250; +case 0xc000021c: return 0x17e6; +case 0xc000021d: return 0x251; +case 0xc000021e: return 0x252; +case 0xc000021f: return 0x253; +case 0xc0000220: return 0x46c; +case 0xc0000221: return 0xc1; +case 0xc0000222: return 0x254; +case 0xc0000223: return 0x255; +case 0xc0000224: return 0x773; +case 0xc0000225: return 0x490; +case 0xc0000226: return 0x256; +case 0xc0000227: return 0x4ff; +case 0xc0000228: return 0x257; +case 0xc0000229: return 0x57; +case 0xc000022a: return 0x1392; +case 0xc000022b: return 0x1392; +case 0xc000022c: return 0x258; +case 0xc000022d: return 0x4d5; +case 0xc000022e: return 0x259; +case 0xc000022f: return 0x25a; +case 0xc0000230: return 0x492; +case 0xc0000231: return 0x25b; +case 0xc0000232: return 0x25c; +case 0xc0000233: return 0x774; +case 0xc0000234: return 0x775; +case 0xc0000235: return 0x6; +case 0xc0000236: return 0x4c9; +case 0xc0000237: return 0x4ca; +case 0xc0000238: return 0x4cb; +case 0xc0000239: return 0x4cc; +case 0xc000023a: return 0x4cd; +case 0xc000023b: return 0x4ce; +case 0xc000023c: return 0x4cf; +case 0xc000023d: return 0x4d0; +case 0xc000023e: return 0x4d1; +case 0xc000023f: return 0x4d2; +case 0xc0000240: return 0x4d3; +case 0xc0000241: return 0x4d4; +case 0xc0000242: return 0x25d; +case 0xc0000243: return 0x4c8; +case 0xc0000244: return 0x25e; +case 0xc0000245: return 0x25f; +case 0xc0000246: return 0x4d6; +case 0xc0000247: return 0x4d7; +case 0xc0000248: return 0x4d8; +case 0xc0000249: return 0xc1; +case 0xc0000250: return 0x260; +case 0xc0000251: return 0x261; +case 0xc0000252: return 0x262; +case 0xc0000253: return 0x4d4; +case 0xc0000254: return 0x263; +case 0xc0000255: return 0x264; +case 0xc0000256: return 0x265; +case 0xc0000257: return 0x4d0; +case 0xc0000258: return 0x266; +case 0xc0000259: return 0x573; +case 0xc000025a: return 0x267; +case 0xc000025b: return 0x268; +case 0xc000025c: return 0x269; +case 0xc000025e: return 0x422; +case 0xc000025f: return 0x26a; +case 0xc0000260: return 0x26b; +case 0xc0000261: return 0x26c; +case 0xc0000262: return 0xb6; +case 0xc0000263: return 0x7f; +case 0xc0000264: return 0x120; +case 0xc0000265: return 0x476; +case 0xc0000266: return 0x26d; +case 0xc0000267: return 0x10fe; +case 0xc0000268: return 0x26e; +case 0xc0000269: return 0x26f; +case 0xc000026a: return 0x1b8e; +case 0xc000026b: return 0x270; +case 0xc000026c: return 0x7d1; +case 0xc000026d: return 0x4b1; +case 0xc000026e: return 0x15; +case 0xc000026f: return 0x21c; +case 0xc0000270: return 0x21c; +case 0xc0000271: return 0x271; +case 0xc0000272: return 0x491; +case 0xc0000273: return 0x272; +case 0xc0000275: return 0x1126; +case 0xc0000276: return 0x1129; +case 0xc0000277: return 0x112a; +case 0xc0000278: return 0x1128; +case 0xc0000279: return 0x780; +case 0xc000027a: return 0x291; +case 0xc000027b: return 0x54f; +case 0xc000027c: return 0x54f; +case 0xc0000280: return 0x781; +case 0xc0000281: return 0xa1; +case 0xc0000282: return 0x273; +case 0xc0000283: return 0x488; +case 0xc0000284: return 0x489; +case 0xc0000285: return 0x48a; +case 0xc0000286: return 0x48b; +case 0xc0000287: return 0x48c; +case 0xc000028a: return 0x5; +case 0xc000028b: return 0x5; +case 0xc000028c: return 0x284; +case 0xc000028d: return 0x5; +case 0xc000028e: return 0x5; +case 0xc000028f: return 0x5; +case 0xc0000290: return 0x5; +case 0xc0000291: return 0x1777; +case 0xc0000292: return 0x1778; +case 0xc0000293: return 0x1772; +case 0xc0000295: return 0x1068; +case 0xc0000296: return 0x1069; +case 0xc0000297: return 0x106a; +case 0xc0000298: return 0x106b; +case 0xc0000299: return 0x201a; +case 0xc000029a: return 0x201b; +case 0xc000029b: return 0x201c; +case 0xc000029c: return 0x1; +case 0xc000029d: return 0x10ff; +case 0xc000029e: return 0x1100; +case 0xc000029f: return 0x494; +case 0xc00002a0: return 0x274; +case 0xc00002a1: return 0x200a; +case 0xc00002a2: return 0x200b; +case 0xc00002a3: return 0x200c; +case 0xc00002a4: return 0x200d; +case 0xc00002a5: return 0x200e; +case 0xc00002a6: return 0x200f; +case 0xc00002a7: return 0x2010; +case 0xc00002a8: return 0x2011; +case 0xc00002a9: return 0x2012; +case 0xc00002aa: return 0x2013; +case 0xc00002ab: return 0x2014; +case 0xc00002ac: return 0x2015; +case 0xc00002ad: return 0x2016; +case 0xc00002ae: return 0x2017; +case 0xc00002af: return 0x2018; +case 0xc00002b0: return 0x2019; +case 0xc00002b1: return 0x211e; +case 0xc00002b2: return 0x1127; +case 0xc00002b3: return 0x275; +case 0xc00002b4: return 0x276; +case 0xc00002b5: return 0x277; +case 0xc00002b6: return 0x651; +case 0xc00002b7: return 0x49a; +case 0xc00002b8: return 0x49b; +case 0xc00002b9: return 0x278; +case 0xc00002ba: return 0x2047; +case 0xc00002c1: return 0x2024; +case 0xc00002c2: return 0x279; +case 0xc00002c3: return 0x575; +case 0xc00002c4: return 0x27a; +case 0xc00002c5: return 0x3e6; +case 0xc00002c6: return 0x1075; +case 0xc00002c7: return 0x1076; +case 0xc00002c8: return 0x27b; +case 0xc00002c9: return 0x4ed; +case 0xc00002ca: return 0x10e8; +case 0xc00002cb: return 0x2138; +case 0xc00002cc: return 0x4e3; +case 0xc00002cd: return 0x2139; +case 0xc00002ce: return 0x27c; +case 0xc00002cf: return 0x49d; +case 0xc00002d0: return 0x213a; +case 0xc00002d1: return 0x27d; +case 0xc00002d2: return 0x27e; +case 0xc00002d3: return 0x15; +case 0xc00002d4: return 0x2141; +case 0xc00002d5: return 0x2142; +case 0xc00002d6: return 0x2143; +case 0xc00002d7: return 0x2144; +case 0xc00002d8: return 0x2145; +case 0xc00002d9: return 0x2146; +case 0xc00002da: return 0x2147; +case 0xc00002db: return 0x2148; +case 0xc00002dc: return 0x2149; +case 0xc00002dd: return 0x32; +case 0xc00002de: return 0x27f; +case 0xc00002df: return 0x2151; +case 0xc00002e0: return 0x2152; +case 0xc00002e1: return 0x2153; +case 0xc00002e2: return 0x2154; +case 0xc00002e3: return 0x215d; +case 0xc00002e4: return 0x2163; +case 0xc00002e5: return 0x2164; +case 0xc00002e6: return 0x2165; +case 0xc00002e7: return 0x216d; +case 0xc00002e8: return 0x280; +case 0xc00002e9: return 0x577; +case 0xc00002ea: return 0x52; +case 0xc00002eb: return 0x281; +case 0xc00002ec: return 0x2171; +case 0xc00002ed: return 0x2172; +case 0xc00002f0: return 0x2; +case 0xc00002fe: return 0x45b; +case 0xc00002ff: return 0x4e7; +case 0xc0000300: return 0x4e6; +case 0xc0000301: return 0x106f; +case 0xc0000302: return 0x1074; +case 0xc0000303: return 0x106e; +case 0xc0000304: return 0x12e; +case 0xc000030c: return 0x792; +case 0xc000030d: return 0x793; +case 0xc0000320: return 0x4ef; +case 0xc0000321: return 0x4f0; +case 0xc0000350: return 0x4e8; +case 0xc0000352: return 0x177d; +case 0xc0000353: return 0x282; +case 0xc0000354: return 0x504; +case 0xc0000355: return 0x283; +case 0xc0000357: return 0x217c; +case 0xc0000358: return 0x2182; +case 0xc0000359: return 0xc1; +case 0xc000035a: return 0xc1; +case 0xc000035c: return 0x572; +case 0xc000035d: return 0x4eb; +case 0xc000035f: return 0x286; +case 0xc0000361: return 0x4ec; +case 0xc0000362: return 0x4ec; +case 0xc0000363: return 0x4ec; +case 0xc0000364: return 0x4ec; +case 0xc0000365: return 0x287; +case 0xc0000366: return 0x288; +case 0xc0000368: return 0x289; +case 0xc0000369: return 0x28a; +case 0xc000036a: return 0x28b; +case 0xc000036b: return 0x4fb; +case 0xc000036c: return 0x4fb; +case 0xc000036d: return 0x28c; +case 0xc000036e: return 0x28d; +case 0xc000036f: return 0x4fc; +case 0xc0000371: return 0x21ac; +case 0xc0000372: return 0x312; +case 0xc0000373: return 0x8; +case 0xc0000374: return 0x54f; +case 0xc0000388: return 0x4f1; +case 0xc000038e: return 0x28e; +case 0xc0000401: return 0x78c; +case 0xc0000402: return 0x78d; +case 0xc0000403: return 0x78e; +case 0xc0000404: return 0x217b; +case 0xc0000405: return 0x219d; +case 0xc0000406: return 0x219f; +case 0xc0000407: return 0x28f; +case 0xc0000408: return 0x52e; +case 0xc0000409: return 0x502; +case 0xc0000410: return 0x503; +case 0xc0000411: return 0x290; +case 0xc0000412: return 0x505; +case 0xc0000413: return 0x78f; +case 0xc0000414: return 0x506; +case 0xc0000416: return 0x8; +case 0xc0000417: return 0x508; +case 0xc0000418: return 0x791; +case 0xc0000419: return 0x215b; +case 0xc000041a: return 0x21ba; +case 0xc000041b: return 0x21bb; +case 0xc000041c: return 0x21bc; +case 0xc000041d: return 0x2c9; +case 0xc0000420: return 0x29c; +case 0xc0000421: return 0x219; +case 0xc0000423: return 0x300; +case 0xc0000424: return 0x4fb; +case 0xc0000425: return 0x3fa; +case 0xc0000426: return 0x301; +case 0xc0000427: return 0x299; +case 0xc0000428: return 0x241; +case 0xc0000429: return 0x307; +case 0xc000042a: return 0x308; +case 0xc000042b: return 0x50c; +case 0xc000042c: return 0x2e4; +case 0xc0000432: return 0x509; +case 0xc0000433: return 0xaa; +case 0xc0000434: return 0xaa; +case 0xc0000435: return 0x4c8; +case 0xc0000441: return 0x1781; +case 0xc0000442: return 0x1782; +case 0xc0000443: return 0x1783; +case 0xc0000444: return 0x1784; +case 0xc0000445: return 0x1785; +case 0xc0000446: return 0x513; +case 0xc0000450: return 0x50b; +case 0xc0000451: return 0x3b92; +case 0xc0000452: return 0x3bc3; +case 0xc0000453: return 0x5bb; +case 0xc0000454: return 0x5be; +case 0xc0000455: return 0x6; +case 0xc0000456: return 0x57; +case 0xc0000457: return 0x57; +case 0xc0000458: return 0x57; +case 0xc0000459: return 0xbea; +case 0xc0000460: return 0x138; +case 0xc0000461: return 0x13a; +case 0xc0000462: return 0x3cfc; +case 0xc0000463: return 0x13c; +case 0xc0000464: return 0x141; +case 0xc0000465: return 0x13b; +case 0xc0000466: return 0x40; +case 0xc0000467: return 0x20; +case 0xc0000468: return 0x142; +case 0xc0000469: return 0x3d00; +case 0xc000046a: return 0x151; +case 0xc000046b: return 0x152; +case 0xc000046c: return 0x153; +case 0xc000046d: return 0x156; +case 0xc000046e: return 0x157; +case 0xc000046f: return 0x158; +case 0xc0000470: return 0x143; +case 0xc0000471: return 0x144; +case 0xc0000472: return 0x146; +case 0xc0000473: return 0x14b; +case 0xc0000474: return 0x147; +case 0xc0000475: return 0x148; +case 0xc0000476: return 0x149; +case 0xc0000477: return 0x14a; +case 0xc0000478: return 0x14c; +case 0xc0000479: return 0x14d; +case 0xc000047a: return 0x14e; +case 0xc000047b: return 0x14f; +case 0xc000047c: return 0x150; +case 0xc000047d: return 0x5b4; +case 0xc000047e: return 0x3d07; +case 0xc000047f: return 0x3d08; +case 0xc0000480: return 0x40; +case 0xc0000481: return 0x7e; +case 0xc0000482: return 0x7e; +case 0xc0000483: return 0x1e3; +case 0xc0000486: return 0x159; +case 0xc0000487: return 0x1f; +case 0xc0000488: return 0x15a; +case 0xc0000489: return 0x3d0f; +case 0xc000048a: return 0x32a; +case 0xc000048b: return 0x32c; +case 0xc000048c: return 0x15b; +case 0xc000048d: return 0x15c; +case 0xc000048e: return 0x162; +case 0xc000048f: return 0x15d; +case 0xc0000490: return 0x491; +case 0xc0000491: return 0x2; +case 0xc0000492: return 0x490; +case 0xc0000493: return 0x492; +case 0xc0000494: return 0x307; +case 0xc0000495: return 0x15; +case 0xc0000496: return 0x163; +case 0xc0000497: return 0x3d5a; +case 0xc0000499: return 0x167; +case 0xc000049a: return 0x168; +case 0xc000049b: return 0x12e; +case 0xc000049c: return 0x169; +case 0xc000049d: return 0x16f; +case 0xc000049e: return 0x170; +case 0xc000049f: return 0x49f; +case 0xc00004a0: return 0x4a0; +case 0xc00004a1: return 0x18f; +case 0xc0000500: return 0x60e; +case 0xc0000501: return 0x60f; +case 0xc0000502: return 0x610; +case 0xc0000503: return 0x15; +case 0xc0000504: return 0x13f; +case 0xc0000505: return 0x140; +case 0xc0000506: return 0x5bf; +case 0xc0000507: return 0xaa; +case 0xc0000508: return 0x5e0; +case 0xc0000509: return 0x5e1; +case 0xc000050b: return 0x112b; +case 0xc000050e: return 0x115c; +case 0xc000050f: return 0x10d3; +case 0xc0000510: return 0x4df; +case 0xc0000511: return 0x32e; +case 0xc0000512: return 0x5; +case 0xc0000513: return 0x180; +case 0xc0000514: return 0x115d; +case 0xc0000602: return 0x675; +case 0xc0000604: return 0x677; +case 0xc0000606: return 0x679; +case 0xc000060a: return 0x67c; +case 0xc000060b: return 0x67d; +case 0xc0000700: return 0x54f; +case 0xc0000701: return 0x54f; +case 0xc0000702: return 0x57; +case 0xc0000703: return 0x54f; +case 0xc0000704: return 0x32; +case 0xc0000705: return 0x57; +case 0xc0000706: return 0x57; +case 0xc0000707: return 0x32; +case 0xc0000708: return 0x54f; +case 0xc0000709: return 0x30b; +case 0xc000070a: return 0x6; +case 0xc000070b: return 0x6; +case 0xc000070c: return 0x6; +case 0xc000070d: return 0x6; +case 0xc000070e: return 0x6; +case 0xc000070f: return 0x6; +case 0xc0000710: return 0x1; +case 0xc0000711: return 0x1; +case 0xc0000712: return 0x50d; +case 0xc0000713: return 0x310; +case 0xc0000714: return 0x52e; +case 0xc0000715: return 0x5b7; +case 0xc0000716: return 0x7b; +case 0xc0000717: return 0x459; +case 0xc0000718: return 0x54f; +case 0xc0000719: return 0x54f; +case 0xc000071a: return 0x54f; +case 0xc000071b: return 0x1; +case 0xc000071c: return 0x57; +case 0xc000071d: return 0x1; +case 0xc000071e: return 0x1; +case 0xc000071f: return 0x1; +case 0xc0000720: return 0x1; +case 0xc0000721: return 0x1; +case 0xc0000722: return 0x72b; +case 0xc0000723: return 0x1f; +case 0xc0000724: return 0x1f; +case 0xc0000725: return 0x1f; +case 0xc0000726: return 0x1f; +case 0xc0000800: return 0x30c; +case 0xc0000801: return 0x21a4; +case 0xc0000802: return 0x50f; +case 0xc0000804: return 0x510; +case 0xc0000805: return 0x1ac1; +case 0xc0000806: return 0x1ac3; +case 0xc0000808: return 0x319; +case 0xc0000809: return 0x31a; +case 0xc000080a: return 0x31b; +case 0xc000080b: return 0x31c; +case 0xc000080c: return 0x31d; +case 0xc000080d: return 0x31e; +case 0xc000080e: return 0x31f; +case 0xc000080f: return 0x4d5; +case 0xc0000810: return 0x328; +case 0xc0000811: return 0x54f; +case 0xc0000901: return 0xdc; +case 0xc0000902: return 0xdd; +case 0xc0000903: return 0xde; +case 0xc0000904: return 0xdf; +case 0xc0000905: return 0xe0; +case 0xc0000906: return 0xe1; +case 0xc0000907: return 0xe2; +case 0xc0000908: return 0x317; +case 0xc0000909: return 0x322; +case 0xc0000910: return 0x326; +case 0xc0009898: return 0x29e; +case 0xc000a002: return 0x17; +case 0xc000a003: return 0x139f; +case 0xc000a004: return 0x154; +case 0xc000a005: return 0x155; +case 0xc000a006: return 0x32b; +case 0xc000a007: return 0x32; +case 0xc000a010: return 0xea; +case 0xc000a011: return 0xea; +case 0xc000a012: return 0x4d0; +case 0xc000a013: return 0x32; +case 0xc000a014: return 0x4d1; +case 0xc000a080: return 0x314; +case 0xc000a081: return 0x315; +case 0xc000a082: return 0x316; +case 0xc000a083: return 0x5b9; +case 0xc000a084: return 0x5ba; +case 0xc000a085: return 0x5bc; +case 0xc000a086: return 0x5bd; +case 0xc000a087: return 0x21bd; +case 0xc000a088: return 0x21be; +case 0xc000a089: return 0x21c6; +case 0xc000a100: return 0x3bc4; +case 0xc000a101: return 0x3bc5; +case 0xc000a121: return 0x3bd9; +case 0xc000a122: return 0x3bda; +case 0xc000a123: return 0x3bdb; +case 0xc000a124: return 0x3bdc; +case 0xc000a125: return 0x3bdd; +case 0xc000a126: return 0x3bde; +case 0xc000a141: return 0x3c28; +case 0xc000a142: return 0x3c29; +case 0xc000a143: return 0x3c2a; +case 0xc000a145: return 0x3c2b; +case 0xc000a146: return 0x3c2c; +case 0xc000a200: return 0x109a; +case 0xc000a201: return 0x109c; +case 0xc000a202: return 0x109d; +case 0xc000a203: return 0x5; +case 0xc000a281: return 0x1130; +case 0xc000a282: return 0x1131; +case 0xc000a283: return 0x1132; +case 0xc000a284: return 0x1133; +case 0xc000a285: return 0x1134; +case 0xc000a2a1: return 0x1158; +case 0xc000a2a2: return 0x1159; +case 0xc000a2a3: return 0x115a; +case 0xc000a2a4: return 0x115b; +case 0xc000ce01: return 0x171; +case 0xc000ce02: return 0x172; +case 0xc000ce03: return 0x173; +case 0xc000ce04: return 0x174; +case 0xc000ce05: return 0x181; +case 0xc000cf00: return 0x166; +case 0xc000cf01: return 0x16a; +case 0xc000cf02: return 0x16b; +case 0xc000cf03: return 0x16c; +case 0xc000cf06: return 0x177; +case 0xc000cf07: return 0x178; +case 0xc000cf08: return 0x179; +case 0xc000cf09: return 0x17a; +case 0xc000cf0a: return 0x17b; +case 0xc000cf0b: return 0x17c; +case 0xc000cf0c: return 0x17d; +case 0xc000cf0d: return 0x17e; +case 0xc000cf0e: return 0x17f; +case 0xc000cf0f: return 0x182; +case 0xc000cf10: return 0x183; +case 0xc000cf11: return 0x184; +case 0xc000cf12: return 0x185; +case 0xc000cf13: return 0x186; +case 0xc000cf14: return 0x187; +case 0xc000cf15: return 0x188; +case 0xc000cf16: return 0x189; +case 0xc000cf17: return 0x18a; +case 0xc000cf18: return 0x18b; +case 0xc000cf19: return 0x18c; +case 0xc000cf1a: return 0x18d; +case 0xc000cf1b: return 0x18e; diff --git a/include/status-code/detail/win32_code_to_generic_code.ipp b/include/status-code/detail/win32_code_to_generic_code.ipp new file mode 100644 index 000000000..fa516050e --- /dev/null +++ b/include/status-code/detail/win32_code_to_generic_code.ipp @@ -0,0 +1,75 @@ +case 0x1: return ENOSYS; +case 0x2: return ENOENT; +case 0x3: return ENOENT; +case 0x4: return EMFILE; +case 0x5: return EACCES; +case 0x6: return EINVAL; +case 0x8: return ENOMEM; +case 0xc: return EACCES; +case 0xe: return ENOMEM; +case 0xf: return ENODEV; +case 0x10: return EACCES; +case 0x11: return EXDEV; +case 0x13: return EACCES; +case 0x14: return ENODEV; +case 0x15: return EAGAIN; +case 0x19: return EIO; +case 0x1d: return EIO; +case 0x1e: return EIO; +case 0x20: return EACCES; +case 0x21: return ENOLCK; +case 0x27: return ENOSPC; +case 0x37: return ENODEV; +case 0x50: return EEXIST; +case 0x52: return EACCES; +case 0x57: return EINVAL; +case 0x6e: return EIO; +case 0x6f: return ENAMETOOLONG; +case 0x70: return ENOSPC; +case 0x7b: return EINVAL; +case 0x83: return EINVAL; +case 0x8e: return EBUSY; +case 0x91: return ENOTEMPTY; +case 0xaa: return EBUSY; +case 0xb7: return EEXIST; +case 0xd4: return ENOLCK; +case 0x10b: return EINVAL; +case 0x3e3: return ECANCELED; +case 0x3e6: return EACCES; +case 0x3f3: return EIO; +case 0x3f4: return EIO; +case 0x3f5: return EIO; +case 0x4d5: return EAGAIN; +case 0x961: return EBUSY; +case 0x964: return EBUSY; +case 0x2714: return EINTR; +case 0x2719: return EBADF; +case 0x271d: return EACCES; +case 0x271e: return EFAULT; +case 0x2726: return EINVAL; +case 0x2728: return EMFILE; +case 0x2733: return EWOULDBLOCK; +case 0x2734: return EINPROGRESS; +case 0x2735: return EALREADY; +case 0x2736: return ENOTSOCK; +case 0x2737: return EDESTADDRREQ; +case 0x2738: return EMSGSIZE; +case 0x2739: return EPROTOTYPE; +case 0x273a: return ENOPROTOOPT; +case 0x273b: return EPROTONOSUPPORT; +case 0x273d: return EOPNOTSUPP; +case 0x273f: return EAFNOSUPPORT; +case 0x2740: return EADDRINUSE; +case 0x2741: return EADDRNOTAVAIL; +case 0x2742: return ENETDOWN; +case 0x2743: return ENETUNREACH; +case 0x2744: return ENETRESET; +case 0x2745: return ECONNABORTED; +case 0x2746: return ECONNRESET; +case 0x2747: return ENOBUFS; +case 0x2748: return EISCONN; +case 0x2749: return ENOTCONN; +case 0x274c: return ETIMEDOUT; +case 0x274d: return ECONNREFUSED; +case 0x274f: return ENAMETOOLONG; +case 0x2751: return EHOSTUNREACH; diff --git a/include/status-code/error.hpp b/include/status-code/error.hpp new file mode 100644 index 000000000..0a4eea709 --- /dev/null +++ b/include/status-code/error.hpp @@ -0,0 +1,59 @@ +/* Proposed SG14 status_code +(C) 2018 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_ERROR_HPP +#define SYSTEM_ERROR2_ERROR_HPP + +#include "errored_status_code.hpp" +#include "system_code.hpp" + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +/*! An errored `system_code` which must be a failure upon copy or move or +non-default construction. The closest equivalent to `std::error_code`, except +it cannot be modified. + +This refines `system_code` into an `error` object meeting the requirements of +[P0709 Zero-overhead deterministic exceptions](https://wg21.link/P0709). + +Differences from `system_code`: + +- Almost always a failure (this is checked at copy or move and non-default +construction, and if not the case, the program is terminated as this is a logic +error) +- Is immutable. + +As with `system_code`, it remains guaranteed to be two CPU registers in size, +and move bitcopying. +*/ +using error = erased_errored_status_code; + +#ifndef NDEBUG +static_assert(sizeof(error) == 2 * sizeof(void*), "error is not exactly two pointers in size!"); +static_assert(traits::is_move_bitcopying::value, "error is not move bitcopying!"); +#endif + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/errored_status_code.hpp b/include/status-code/errored_status_code.hpp new file mode 100644 index 000000000..26d3df0fa --- /dev/null +++ b/include/status-code/errored_status_code.hpp @@ -0,0 +1,442 @@ +/* Proposed SG14 status_code +(C) 2018-2026 Niall Douglas (5 commits) +File Created: Jun 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_ERRORED_STATUS_CODE_HPP +#define SYSTEM_ERROR2_ERRORED_STATUS_CODE_HPP + +#include "quick_status_code_from_enum.hpp" + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +/*! A `status_code` which is always a failure. The closest equivalent to +`std::error_code`, except it cannot be modified, and is templated. + +Differences from `status_code`: + +- Never successful (this contract is checked on construction, if fails then it +terminates the process). +- Is immutable. +*/ +template class errored_status_code : public status_code +{ + using _base = status_code; + using _base::clear; + using _base::success; + + void _check() + { + if(_base::success()) + { + std::terminate(); + } + } + +public: + //! The type of the domain. + using typename _base::domain_type; + //! The type of the error code. + using typename _base::value_type; + //! The type of a reference to a message string. + using typename _base::string_ref; + + //! Default constructor. + errored_status_code() = default; + //! Copy constructor. + errored_status_code(const errored_status_code &) = default; + //! Move constructor. + errored_status_code(errored_status_code &&) = default; // NOLINT + //! Copy assignment. + errored_status_code &operator=(const errored_status_code &) = default; + //! Move assignment. + errored_status_code &operator=(errored_status_code &&) = default; // NOLINT + ~errored_status_code() = default; + + //! Explicitly construct from any similarly erased status code + explicit errored_status_code(const _base &o) noexcept(std::is_nothrow_copy_constructible<_base>::value) + : _base(o) + { + _check(); + } + //! Explicitly construct from any similarly erased status code + explicit errored_status_code(_base &&o) noexcept(std::is_nothrow_move_constructible<_base>::value) + : _base(static_cast<_base &&>(o)) + { + _check(); + } + + /***** KEEP THESE IN SYNC WITH STATUS_CODE *****/ + //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a + //! `status_code`. + SYSTEM_ERROR2_TEMPLATE(class T, class... Args, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + T, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(!std::is_same::type, errored_status_code>::value // not copy/move of self + && !std::is_same::type, in_place_t>::value // not in_place_t + && is_status_code::value // ADL makes a status code + && std::is_constructible<_base, MakeStatusCodeResult>::value)) // ADLed status code is compatible + errored_status_code(T &&v, + Args &&...args) noexcept(detail::safe_get_make_status_code_noexcept::value) // NOLINT + : _base(make_status_code(static_cast(v), static_cast(args)...)) + { + _check(); + } + + //! Implicit construction from any `quick_status_code_from_enum` enumerated type. + SYSTEM_ERROR2_TEMPLATE( + class Enum, // + class QuickStatusCodeType = typename quick_status_code_from_enum::code_type) // Enumeration has been activated + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( + std::is_constructible::value)) // Its status code is compatible + errored_status_code(Enum &&v) noexcept( + std::is_nothrow_constructible::value) // NOLINT + : errored_status_code(QuickStatusCodeType(static_cast(v))) + { + _check(); + } + //! Explicit in-place construction. + template + explicit errored_status_code(in_place_t _, + Args &&...args) noexcept(std::is_nothrow_constructible::value) + : _base(_, static_cast(args)...) + { + _check(); + } + //! Explicit in-place construction from initialiser list. + template + explicit errored_status_code(in_place_t _, std::initializer_list il, Args &&...args) noexcept( + std::is_nothrow_constructible, Args &&...>::value) + : _base(_, il, static_cast(args)...) + { + _check(); + } + //! Explicit copy construction from a `value_type`. + explicit errored_status_code(const value_type &v) noexcept(std::is_nothrow_copy_constructible::value) + : _base(v) + { + _check(); + } + //! Explicit move construction from a `value_type`. + explicit errored_status_code(value_type &&v) noexcept(std::is_nothrow_move_constructible::value) + : _base(static_cast(v)) + { + _check(); + } + /*! Explicit construction from an erased status code. Available only if + `value_type` is trivially copyable or move bitcopying, and `sizeof(status_code) <= sizeof(status_code>)`. + Does not check if domains are equal. + */ + SYSTEM_ERROR2_TEMPLATE(class ErasedType) // + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe>::value)) + explicit errored_status_code(const status_code> &v) noexcept( + std::is_nothrow_copy_constructible::value) + : errored_status_code(detail::erasure_cast(v.value())) // NOLINT + { + assert(v.domain() == this->domain()); // NOLINT + _check(); + } + + //! Always false (including at compile time), as errored status codes are never successful. + constexpr bool success() const noexcept { return false; } + //! Return a const reference to the `value_type`. + constexpr const value_type &value() const & noexcept { return this->_value; } +}; + +namespace traits +{ + template struct is_move_bitcopying> + { + static constexpr bool value = is_move_bitcopying::value; + }; +} // namespace traits + +template +class errored_status_code> : public status_code> +{ + using _base = status_code>; + using _base::success; + + void _check() + { + if(_base::success()) + { + std::terminate(); + } + } + +public: + using domain_type = typename _base::domain_type; + using value_type = typename _base::value_type; + using string_ref = typename _base::string_ref; + + //! Default construction to empty + errored_status_code() = default; + //! Copy constructor + errored_status_code(const errored_status_code &) = default; + //! Move constructor + errored_status_code(errored_status_code &&) = default; // NOLINT + //! Copy assignment + errored_status_code &operator=(const errored_status_code &) = default; + //! Move assignment + errored_status_code &operator=(errored_status_code &&) = default; // NOLINT + ~errored_status_code() = default; + + //! Explicitly construct from any similarly erased status code + explicit errored_status_code(const _base &o) noexcept(std::is_nothrow_copy_constructible<_base>::value) + : _base(o) + { + _check(); + } + //! Explicitly construct from any similarly erased status code + explicit errored_status_code(_base &&o) noexcept(std::is_nothrow_move_constructible<_base>::value) + : _base(static_cast<_base &&>(o)) + { + _check(); + } + + /***** KEEP THESE IN SYNC WITH STATUS_CODE *****/ + //! Implicit copy construction from any other status code if its value type is trivially copyable, it would fit into + //! our storage, and it is not an erased status code. + SYSTEM_ERROR2_TEMPLATE(class DomainType) // + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), + SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code::type>>::value)) + errored_status_code(const status_code &v) noexcept + : _base(v) // NOLINT + { + _check(); + } + //! Implicit copy construction from any other status code if its value type is trivially copyable and it would fit + //! into our storage, and it is not an erased status code. + SYSTEM_ERROR2_TEMPLATE(class DomainType) // + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value), + SYSTEM_ERROR2_TPRED(!detail::is_erased_status_code::type>>::value)) + errored_status_code(const errored_status_code &v) noexcept + : _base(static_cast &>(v)) // NOLINT + { + _check(); + } + //! Implicit move construction from any other status code if its value type is trivially copyable or move bitcopying + //! and it would fit into our storage + SYSTEM_ERROR2_TEMPLATE(class DomainType) // + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) + errored_status_code(status_code &&v) noexcept + : _base(static_cast &&>(v)) // NOLINT + { + _check(); + } + //! Implicit move construction from any other status code if its value type is trivially copyable or move bitcopying + //! and it would fit into our storage + SYSTEM_ERROR2_TEMPLATE(class DomainType) // + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(detail::domain_value_type_erasure_is_safe, DomainType>::value)) + errored_status_code(errored_status_code &&v) noexcept + : _base(static_cast &&>(v)) // NOLINT + { + _check(); + } + //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a + //! `status_code`. + SYSTEM_ERROR2_TEMPLATE(class T, class... Args, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + T, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found + SYSTEM_ERROR2_TREQUIRES( + SYSTEM_ERROR2_TPRED(!std::is_same::type, errored_status_code>::value // not copy/move of self + && !std::is_same::type, value_type>::value // not copy/move of value type + && is_status_code::value // ADL makes a status code + && std::is_constructible<_base, MakeStatusCodeResult>::value)) // ADLed status code is compatible + errored_status_code(T &&v, + Args &&...args) noexcept(detail::safe_get_make_status_code_noexcept::value) // NOLINT + : _base(make_status_code(static_cast(v), static_cast(args)...)) + { + _check(); + } + //! Implicit construction from any `quick_status_code_from_enum` enumerated type. + SYSTEM_ERROR2_TEMPLATE( + class Enum, // + class QuickStatusCodeType = typename quick_status_code_from_enum::code_type) // Enumeration has been activated + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( + std::is_constructible::value)) // Its status code is compatible + errored_status_code(Enum &&v) noexcept( + std::is_nothrow_constructible::value) // NOLINT + : errored_status_code(QuickStatusCodeType(static_cast(v))) + { + _check(); + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + //! Explicit copy construction from an unknown status code. Note that this will be empty if its value type is not + //! trivially copyable or would not fit into our storage or the source domain's `_do_erased_copy()` refused the copy. + explicit errored_status_code(in_place_t _, const status_code &v) // NOLINT + : _base(_, v) + { + _check(); + } +#endif + //! Tagged copy construction from an unknown status code. Note that this will be empty if its value type is not + //! trivially copyable or would not fit into our storage or the source domain's `_do_erased_copy()` refused the copy. + SYSTEM_ERROR2_CONSTEXPR20 errored_status_code(std::nothrow_t _, const status_code &v) noexcept // NOLINT + : _base(_, v) + { + _check(); + } + + //! Always false (including at compile time), as errored status codes are never successful. + constexpr bool success() const noexcept { return false; } + //! Return the erased `value_type` by value. + constexpr value_type value() const noexcept { return this->_value; } +}; +/*! An erased type specialisation of `errored_status_code`. +Available only if `ErasedType` satisfies `traits::is_move_bitcopying::value`. +*/ +template using erased_errored_status_code = errored_status_code>; + + +namespace traits +{ + template struct is_move_bitcopying>> + { + static constexpr bool value = true; + }; +} // namespace traits + + +//! True if the status code's are semantically equal via `equivalent()`. +template +inline bool operator==(const errored_status_code &a, const errored_status_code &b) noexcept +{ + return a.equivalent(static_cast &>(b)); +} +//! True if the status code's are semantically equal via `equivalent()`. +template +inline bool operator==(const status_code &a, const errored_status_code &b) noexcept +{ + return a.equivalent(static_cast &>(b)); +} +//! True if the status code's are semantically equal via `equivalent()`. +template +inline bool operator==(const errored_status_code &a, const status_code &b) noexcept +{ + return static_cast &>(a).equivalent(b); +} +//! True if the status code's are not semantically equal via `equivalent()`. +template +inline bool operator!=(const errored_status_code &a, const errored_status_code &b) noexcept +{ + return !a.equivalent(static_cast &>(b)); +} +//! True if the status code's are not semantically equal via `equivalent()`. +template +inline bool operator!=(const status_code &a, const errored_status_code &b) noexcept +{ + return !a.equivalent(static_cast &>(b)); +} +//! True if the status code's are not semantically equal via `equivalent()`. +template +inline bool operator!=(const errored_status_code &a, const status_code &b) noexcept +{ + return !static_cast &>(a).equivalent(b); +} +//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +inline bool operator==(const errored_status_code &a, const T &b) +{ + return a.equivalent(make_status_code(b)); +} +//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +inline bool operator==(const T &a, const errored_status_code &b) +{ + return b.equivalent(make_status_code(a)); +} +//! True if the status code's are not semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +inline bool operator!=(const errored_status_code &a, const T &b) +{ + return !a.equivalent(make_status_code(b)); +} +//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +inline bool operator!=(const T &a, const errored_status_code &b) +{ + return !b.equivalent(make_status_code(a)); +} +//! True if the status code's are semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(b)`. +template ::code_type // Enumeration has been + // activated + > +inline bool operator==(const errored_status_code &a, const T &b) +{ + return a.equivalent(QuickStatusCodeType(b)); +} +//! True if the status code's are semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(a)`. +template ::code_type // Enumeration has been + // activated + > +inline bool operator==(const T &a, const errored_status_code &b) +{ + return b.equivalent(QuickStatusCodeType(a)); +} +//! True if the status code's are not semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(b)`. +template ::code_type // Enumeration has been + // activated + > +inline bool operator!=(const errored_status_code &a, const T &b) +{ + return !a.equivalent(QuickStatusCodeType(b)); +} +//! True if the status code's are not semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(a)`. +template ::code_type // Enumeration has been + // activated + > +inline bool operator!=(const T &a, const errored_status_code &b) +{ + return !b.equivalent(QuickStatusCodeType(a)); +} + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/generic_code.hpp b/include/status-code/generic_code.hpp new file mode 100644 index 000000000..a64ee17a7 --- /dev/null +++ b/include/status-code/generic_code.hpp @@ -0,0 +1,561 @@ +/* Proposed SG14 status_code +(C) 2018 - 2026 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_GENERIC_CODE_HPP +#define SYSTEM_ERROR2_GENERIC_CODE_HPP + +#include "status_error.hpp" + +#include // for error constants + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +//! The generic error coding (POSIX) +enum class errc : int +{ + success = 0, + unknown = -1, + + address_family_not_supported = EAFNOSUPPORT, + address_in_use = EADDRINUSE, + address_not_available = EADDRNOTAVAIL, + already_connected = EISCONN, + argument_list_too_long = E2BIG, + argument_out_of_domain = EDOM, + bad_address = EFAULT, + bad_file_descriptor = EBADF, + bad_message = EBADMSG, + broken_pipe = EPIPE, + connection_aborted = ECONNABORTED, + connection_already_in_progress = EALREADY, + connection_refused = ECONNREFUSED, + connection_reset = ECONNRESET, + cross_device_link = EXDEV, + destination_address_required = EDESTADDRREQ, + device_or_resource_busy = EBUSY, + directory_not_empty = ENOTEMPTY, + executable_format_error = ENOEXEC, + file_exists = EEXIST, + file_too_large = EFBIG, + filename_too_long = ENAMETOOLONG, + function_not_supported = ENOSYS, + host_unreachable = EHOSTUNREACH, + identifier_removed = EIDRM, + illegal_byte_sequence = EILSEQ, + inappropriate_io_control_operation = ENOTTY, + interrupted = EINTR, + invalid_argument = EINVAL, + invalid_seek = ESPIPE, + io_error = EIO, + is_a_directory = EISDIR, + message_size = EMSGSIZE, + network_down = ENETDOWN, + network_reset = ENETRESET, + network_unreachable = ENETUNREACH, + no_buffer_space = ENOBUFS, + no_child_process = ECHILD, + no_link = ENOLINK, + no_lock_available = ENOLCK, + no_message = ENOMSG, + no_protocol_option = ENOPROTOOPT, + no_space_on_device = ENOSPC, + no_stream_resources = ENOSR, + no_such_device_or_address = ENXIO, + no_such_device = ENODEV, + no_such_file_or_directory = ENOENT, + no_such_process = ESRCH, + not_a_directory = ENOTDIR, + not_a_socket = ENOTSOCK, + not_a_stream = ENOSTR, + not_connected = ENOTCONN, + not_enough_memory = ENOMEM, + not_supported = ENOTSUP, + operation_canceled = ECANCELED, + operation_in_progress = EINPROGRESS, + operation_not_permitted = EPERM, + operation_not_supported = EOPNOTSUPP, + operation_would_block = EWOULDBLOCK, + owner_dead = EOWNERDEAD, + permission_denied = EACCES, + protocol_error = EPROTO, + protocol_not_supported = EPROTONOSUPPORT, + read_only_file_system = EROFS, + resource_deadlock_would_occur = EDEADLK, + resource_unavailable_try_again = EAGAIN, + result_out_of_range = ERANGE, + state_not_recoverable = ENOTRECOVERABLE, + stream_timeout = ETIME, + text_file_busy = ETXTBSY, + timed_out = ETIMEDOUT, + too_many_files_open_in_system = ENFILE, + too_many_files_open = EMFILE, + too_many_links = EMLINK, + too_many_symbolic_link_levels = ELOOP, + value_too_large = EOVERFLOW, + wrong_protocol_type = EPROTOTYPE +}; + +namespace detail +{ + SYSTEM_ERROR2_CONSTEXPR14 inline const char *generic_code_message(errc code) noexcept + { + switch(code) + { + case errc::success: + return "Success"; + case errc::address_family_not_supported: + return "Address family not supported by protocol"; + case errc::address_in_use: + return "Address already in use"; + case errc::address_not_available: + return "Cannot assign requested address"; + case errc::already_connected: + return "Transport endpoint is already connected"; + case errc::argument_list_too_long: + return "Argument list too long"; + case errc::argument_out_of_domain: + return "Numerical argument out of domain"; + case errc::bad_address: + return "Bad address"; + case errc::bad_file_descriptor: + return "Bad file descriptor"; + case errc::bad_message: + return "Bad message"; + case errc::broken_pipe: + return "Broken pipe"; + case errc::connection_aborted: + return "Software caused connection abort"; + case errc::connection_already_in_progress: + return "Operation already in progress"; + case errc::connection_refused: + return "Connection refused"; + case errc::connection_reset: + return "Connection reset by peer"; + case errc::cross_device_link: + return "Invalid cross-device link"; + case errc::destination_address_required: + return "Destination address required"; + case errc::device_or_resource_busy: + return "Device or resource busy"; + case errc::directory_not_empty: + return "Directory not empty"; + case errc::executable_format_error: + return "Exec format error"; + case errc::file_exists: + return "File exists"; + case errc::file_too_large: + return "File too large"; + case errc::filename_too_long: + return "File name too long"; + case errc::function_not_supported: + return "Function not implemented"; + case errc::host_unreachable: + return "No route to host"; + case errc::identifier_removed: + return "Identifier removed"; + case errc::illegal_byte_sequence: + return "Invalid or incomplete multibyte or wide character"; + case errc::inappropriate_io_control_operation: + return "Inappropriate ioctl for device"; + case errc::interrupted: + return "Interrupted system call"; + case errc::invalid_argument: + return "Invalid argument"; + case errc::invalid_seek: + return "Illegal seek"; + case errc::io_error: + return "Input/output error"; + case errc::is_a_directory: + return "Is a directory"; + case errc::message_size: + return "Message too long"; + case errc::network_down: + return "Network is down"; + case errc::network_reset: + return "Network dropped connection on reset"; + case errc::network_unreachable: + return "Network is unreachable"; + case errc::no_buffer_space: + return "No buffer space available"; + case errc::no_child_process: + return "No child processes"; + case errc::no_link: + return "Link has been severed"; + case errc::no_lock_available: + return "No locks available"; + case errc::no_message: + return "No message of desired type"; + case errc::no_protocol_option: + return "Protocol not available"; + case errc::no_space_on_device: + return "No space left on device"; + case errc::no_stream_resources: + return "Out of streams resources"; + case errc::no_such_device_or_address: + return "No such device or address"; + case errc::no_such_device: + return "No such device"; + case errc::no_such_file_or_directory: + return "No such file or directory"; + case errc::no_such_process: + return "No such process"; + case errc::not_a_directory: + return "Not a directory"; + case errc::not_a_socket: + return "Socket operation on non-socket"; + case errc::not_a_stream: + return "Device not a stream"; + case errc::not_connected: + return "Transport endpoint is not connected"; + case errc::not_enough_memory: + return "Cannot allocate memory"; +#if ENOTSUP != EOPNOTSUPP + case errc::not_supported: + return "Operation not supported"; +#endif + case errc::operation_canceled: + return "Operation canceled"; + case errc::operation_in_progress: + return "Operation now in progress"; + case errc::operation_not_permitted: + return "Operation not permitted"; + case errc::operation_not_supported: + return "Operation not supported"; +#if EAGAIN != EWOULDBLOCK + case errc::operation_would_block: + return "Resource temporarily unavailable"; +#endif + case errc::owner_dead: + return "Owner died"; + case errc::permission_denied: + return "Permission denied"; + case errc::protocol_error: + return "Protocol error"; + case errc::protocol_not_supported: + return "Protocol not supported"; + case errc::read_only_file_system: + return "Read-only file system"; + case errc::resource_deadlock_would_occur: + return "Resource deadlock avoided"; + case errc::resource_unavailable_try_again: + return "Resource temporarily unavailable"; + case errc::result_out_of_range: + return "Numerical result out of range"; + case errc::state_not_recoverable: + return "State not recoverable"; + case errc::stream_timeout: + return "Timer expired"; + case errc::text_file_busy: + return "Text file busy"; + case errc::timed_out: + return "Connection timed out"; + case errc::too_many_files_open_in_system: + return "Too many open files in system"; + case errc::too_many_files_open: + return "Too many open files"; + case errc::too_many_links: + return "Too many links"; + case errc::too_many_symbolic_link_levels: + return "Too many levels of symbolic links"; + case errc::value_too_large: + return "Value too large for defined data type"; + case errc::wrong_protocol_type: + return "Protocol wrong type for socket"; + default: + return "unknown"; + } + } +} // namespace detail + +/*! The implementation of the domain for generic status codes, those mapped by `errc` (POSIX). + */ +class _generic_code_domain : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + +public: + //! The value type of the generic code, which is an `errc` as per POSIX. + using value_type = errc; + using string_ref = _base::string_ref; + +public: + //! Default constructor + constexpr explicit _generic_code_domain(typename _base::unique_id_type id = 0x746d6354f4f733e9) noexcept + : _base(id) + { + } + _generic_code_domain(const _generic_code_domain &) = default; + _generic_code_domain(_generic_code_domain &&) = default; + _generic_code_domain &operator=(const _generic_code_domain &) = default; + _generic_code_domain &operator=(_generic_code_domain &&) = default; + ~_generic_code_domain() = default; + + //! Constexpr singleton getter. Returns the constexpr generic_code_domain variable. + static inline constexpr const _generic_code_domain &get(); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref("generic domain"); + return 0; + } // NOLINT + + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); // NOLINT + return static_cast(code).value() != errc::success; // NOLINT + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool + _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); // NOLINT + const auto &c1 = static_cast(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + return c1.value() == c2.value(); + } + return false; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual inline void + _do_generic_code(_vtable_generic_code_args &args) const noexcept override; + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + args.ret = string_ref(detail::generic_code_message(c.value())); + return 0; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); // NOLINT + const auto &c = static_cast(code); // NOLINT + throw status_error<_generic_code_domain>(c); + } +#endif +}; +//! A specialisation of `status_error` for the generic code domain. +using generic_error = status_error<_generic_code_domain>; +//! A constexpr source variable for the generic code domain, which is that of `errc` (POSIX). Returned by +//! `_generic_code_domain::get()`. +constexpr _generic_code_domain generic_code_domain; +inline constexpr const _generic_code_domain &_generic_code_domain::get() +{ + return generic_code_domain; +} +// Enable implicit construction of generic_code from errc +SYSTEM_ERROR2_CONSTEXPR14 inline generic_code make_status_code(errc c) noexcept +{ + return generic_code(in_place, c); +} + +namespace detail +{ + SYSTEM_ERROR2_CONSTEXPR20 inline void generic_code_check_throw(int errcode) + { + if(errcode != 0) + { + const generic_code c(in_place, (errc) errcode); +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + c.throw_exception(); +#else + const auto msg = c.message(); + SYSTEM_ERROR2_FATAL(msg.c_str()); +#endif + } + } +} // namespace detail + +struct status_code_domain::_vtable_generic_code_args +{ + generic_code ret; + const status_code &code; + + constexpr _vtable_generic_code_args(generic_code _ret, const status_code &_code) + : ret(_ret) + , code(_code) + { + } +}; + +SYSTEM_ERROR2_CONSTEXPR20 inline void +_generic_code_domain::_do_generic_code(_vtable_generic_code_args &args) const noexcept +{ + assert(args.code.domain() == *this); // NOLINT + args.ret = static_cast(args.code); // NOLINT +} + +SYSTEM_ERROR2_CONSTEXPR20 inline generic_code +status_code_domain::_generic_code(const status_code &code) const noexcept +{ + _vtable_generic_code_args args{{}, code}; + _do_generic_code(args); + return args.ret; +} + + +/*************************************************************************************************************/ + + +template +inline SYSTEM_ERROR2_CONSTEXPR14 bool status_code::equivalent(const status_code &o) const noexcept +{ + if(_domain && o._domain) + { + if(_domain->_do_equivalent(*this, o)) + { + return true; + } + if(o._domain->_do_equivalent(o, *this)) + { + return true; + } + generic_code c1 = o._domain->_generic_code(o); + if(c1.value() != errc::unknown && _domain->_do_equivalent(*this, c1)) + { + return true; + } + generic_code c2 = _domain->_generic_code(*this); + if(c2.value() != errc::unknown && o._domain->_do_equivalent(o, c2)) + { + return true; + } + } + // If we are both empty, we are equivalent, otherwise not equivalent + return (!_domain && !o._domain); +} +//! True if the status code's are semantically equal via `equivalent()`. +template +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code &a, + const status_code &b) noexcept +{ + return a.equivalent(b); +} +//! True if the status code's are not semantically equal via `equivalent()`. +template +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code &a, + const status_code &b) noexcept +{ + return !a.equivalent(b); +} +//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code &a, const T &b) +{ + return a.equivalent(make_status_code(b)); +} +//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const T &a, const status_code &b) +{ + return b.equivalent(make_status_code(a)); +} +//! True if the status code's are not semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class DomainType1, class T, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code &a, const T &b) +{ + return !a.equivalent(make_status_code(b)); +} +//! True if the status code's are semantically equal via `equivalent()` to `make_status_code(T)`. +SYSTEM_ERROR2_TEMPLATE(class T, class DomainType1, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + const T &>::type) // Safe ADL lookup of make_status_code(), returns void if not found +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // ADL makes a status code +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const status_code &b) +{ + return !b.equivalent(make_status_code(a)); +} +//! True if the status code's are semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(b)`. +template ::code_type // Enumeration has been + // activated + > +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const status_code &a, const T &b) +{ + return a.equivalent(QuickStatusCodeType(b)); +} +//! True if the status code's are semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(a)`. +template ::code_type // Enumeration has been + // activated + > +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator==(const T &a, const status_code &b) +{ + return b.equivalent(QuickStatusCodeType(a)); +} +//! True if the status code's are not semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(b)`. +template ::code_type // Enumeration has been + // activated + > +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const status_code &a, const T &b) +{ + return !a.equivalent(QuickStatusCodeType(b)); +} +//! True if the status code's are not semantically equal via `equivalent()` to +//! `quick_status_code_from_enum::code_type(a)`. +template ::code_type // Enumeration has been + // activated + > +SYSTEM_ERROR2_CONSTEXPR14 inline bool operator!=(const T &a, const status_code &b) +{ + return !b.equivalent(QuickStatusCodeType(a)); +} + + +SYSTEM_ERROR2_NAMESPACE_END + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + +#endif diff --git a/include/status-code/getaddrinfo_code.hpp b/include/status-code/getaddrinfo_code.hpp new file mode 100644 index 000000000..bc077a1ff --- /dev/null +++ b/include/status-code/getaddrinfo_code.hpp @@ -0,0 +1,175 @@ +/* Proposed SG14 status_code +(C) 2020 Niall Douglas (5 commits) +File Created: Jan 2020 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_GETADDRINFO_CODE_HPP +#define SYSTEM_ERROR2_GETADDRINFO_CODE_HPP + +#include "quick_status_code_from_enum.hpp" + +#ifdef _WIN32 +#error Not available for Microsoft Windows +#else +#include +#include +#include +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +class _getaddrinfo_code_domain; +//! A getaddrinfo error code, those returned by `getaddrinfo()`. +using getaddrinfo_code = status_code<_getaddrinfo_code_domain>; +//! A specialisation of `status_error` for the `getaddrinfo()` error code domain. +using getaddrinfo_error = status_error<_getaddrinfo_code_domain>; + +/*! The implementation of the domain for `getaddrinfo()` error codes, those returned by `getaddrinfo()`. + */ +class _getaddrinfo_code_domain : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + +public: + //! The value type of the `getaddrinfo()` code, which is an `int` + using value_type = int; + using _base::string_ref; + + //! Default constructor + constexpr explicit _getaddrinfo_code_domain(typename _base::unique_id_type id = 0x5b24b2de470ff7b6) noexcept + : _base(id) + { + } + _getaddrinfo_code_domain(const _getaddrinfo_code_domain &) = default; + _getaddrinfo_code_domain(_getaddrinfo_code_domain &&) = default; + _getaddrinfo_code_domain &operator=(const _getaddrinfo_code_domain &) = default; + _getaddrinfo_code_domain &operator=(_getaddrinfo_code_domain &&) = default; + ~_getaddrinfo_code_domain() = default; + + //! Constexpr singleton getter. Returns constexpr getaddrinfo_code_domain variable. + static inline constexpr const _getaddrinfo_code_domain &get(); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref("getaddrinfo() domain"); + return 0; + } // NOLINT + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); // NOLINT + return static_cast(code).value() != 0; // NOLINT + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool + _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); // NOLINT + const auto &c1 = static_cast(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + return c1.value() == c2.value(); + } + return false; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + switch(c.value()) + { +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: + args.ret = errc::no_such_device_or_address; + return; +#endif + case EAI_FAIL: + args.ret = errc::io_error; + return; + case EAI_MEMORY: + args.ret = errc::not_enough_memory; + return; +#ifdef EAI_NODATA + case EAI_NODATA: + args.ret = errc::no_such_device_or_address; + return; +#endif + case EAI_NONAME: + args.ret = errc::no_such_device_or_address; + return; +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: + args.ret = errc::argument_list_too_long; + return; +#endif + case EAI_BADFLAGS: // fallthrough + case EAI_SERVICE: + args.ret = errc::invalid_argument; + return; + case EAI_FAMILY: // fallthrough + case EAI_SOCKTYPE: + args.ret = errc::operation_not_supported; + return; + case EAI_AGAIN: // fallthrough + case EAI_SYSTEM: + args.ret = errc::resource_unavailable_try_again; + return; + default: + args.ret = errc::unknown; + return; + } + } + virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + args.ret = string_ref(gai_strerror(c.value())); + return 0; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); // NOLINT + const auto &c = static_cast(code); // NOLINT + throw status_error<_getaddrinfo_code_domain>(c); + } +#endif +}; +//! A constexpr source variable for the `getaddrinfo()` code domain, which is that of `getaddrinfo()`. Returned by +//! `_getaddrinfo_code_domain::get()`. +constexpr _getaddrinfo_code_domain getaddrinfo_code_domain; +inline constexpr const _getaddrinfo_code_domain &_getaddrinfo_code_domain::get() +{ + return getaddrinfo_code_domain; +} + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/http_status_code.hpp b/include/status-code/http_status_code.hpp new file mode 100644 index 000000000..066171da7 --- /dev/null +++ b/include/status-code/http_status_code.hpp @@ -0,0 +1,358 @@ +/* Proposed SG14 status_code +(C) 2022 Niall Douglas (5 commits) +File Created: Jun 2022 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_HTTP_STATUS_CODE_HPP +#define SYSTEM_ERROR2_HTTP_STATUS_CODE_HPP + +#include "generic_code.hpp" +#include "status_code.hpp" + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +class _http_status_code_domain; +//! A HTTP status code. +using http_status_code = status_code<_http_status_code_domain>; + +namespace mixins +{ + template struct mixin : public Base + { + using Base::Base; + + //! True if the HTTP status code is informational + inline bool is_http_informational() const noexcept; + //! True if the HTTP status code is successful + inline bool is_http_success() const noexcept; + //! True if the HTTP status code is redirection + inline bool is_http_redirection() const noexcept; + //! True if the HTTP status code is client error + inline bool is_http_client_error() const noexcept; + //! True if the HTTP status code is server error + inline bool is_http_server_error() const noexcept; + }; +} // namespace mixins + +/*! The implementation of the domain for HTTP status codes. + */ +class _http_status_code_domain : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + +public: + //! The value type of the HTTP code, which is an `int` + using value_type = int; + using _base::string_ref; + + //! Default constructor + constexpr explicit _http_status_code_domain(typename _base::unique_id_type id = 0xbdb4cde88378a333ull) noexcept + : _base(id) + { + } + _http_status_code_domain(const _http_status_code_domain &) = default; + _http_status_code_domain(_http_status_code_domain &&) = default; + _http_status_code_domain &operator=(const _http_status_code_domain &) = default; + _http_status_code_domain &operator=(_http_status_code_domain &&) = default; + ~_http_status_code_domain() = default; + + //! Constexpr singleton getter. Returns constexpr http_status_code_domain variable. + static inline constexpr const _http_status_code_domain &get(); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref("HTTP status domain"); + return 0; + } // NOLINT + + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); // NOLINT + return static_cast(code).value() >= 400; // NOLINT + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool + _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); // NOLINT + const auto &c1 = static_cast(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + return c1.value() == c2.value(); + } + return false; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual void + _do_generic_code(_vtable_generic_code_args &args) const noexcept override // NOLINT + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + switch(c.value()) + { + case 102: + case 202: + args.ret = errc::operation_in_progress; + break; + case 400: + args.ret = errc::invalid_argument; + break; + case 401: + args.ret = errc::operation_not_permitted; + break; + case 403: + args.ret = errc::permission_denied; + break; + case 404: + case 410: + args.ret = errc::no_such_file_or_directory; + break; + case 405: + case 418: + args.ret = errc::operation_not_supported; + break; + case 406: + args.ret = errc::protocol_not_supported; + break; + case 408: + args.ret = errc::timed_out; + break; + case 413: + args.ret = errc::result_out_of_range; + break; + case 501: + args.ret = errc::not_supported; + break; + case 503: + args.ret = errc::resource_unavailable_try_again; + break; + case 504: + args.ret = errc::timed_out; + break; + case 507: + args.ret = errc::no_space_on_device; + break; + default: + args.ret = errc::unknown; + break; + } + } + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_message(_vtable_message_args &args) const noexcept override // NOLINT + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + args.ret = string_ref( + [&]() -> const char * + { + switch(c.value()) + { + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 102: + return "Processing"; + case 103: + return "Early Hints"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 209: + return "IM Used"; + case 300: + return "Multiple Choices"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "Switch Proxy"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 418: + return "I'm a teapot"; + case 421: + return "Misdirected Request"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 425: + return "Too Early"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; + default: + return "Unknown"; + } + }()); + return 0; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); // NOLINT + const auto &c = static_cast(code); // NOLINT + throw status_error<_http_status_code_domain>(c); + } +#endif +}; +//! A constexpr source variable for the `getaddrinfo()` code domain, which is that of `getaddrinfo()`. Returned by +//! `_http_status_code_domain::get()`. +constexpr _http_status_code_domain http_status_code_domain; +inline constexpr const _http_status_code_domain &_http_status_code_domain::get() +{ + return http_status_code_domain; +} + +namespace mixins +{ + template inline bool mixin::is_http_informational() const noexcept + { + const auto &c = static_cast(this)->value(); + return c >= 100 && c < 200; + } + template inline bool mixin::is_http_success() const noexcept + { + const auto &c = static_cast(this)->value(); + return c >= 200 && c < 300; + } + template inline bool mixin::is_http_redirection() const noexcept + { + const auto &c = static_cast(this)->value(); + return c >= 300 && c < 400; + } + template inline bool mixin::is_http_client_error() const noexcept + { + const auto &c = static_cast(this)->value(); + return c >= 400 && c < 500; + } + template inline bool mixin::is_http_server_error() const noexcept + { + const auto &c = static_cast(this)->value(); + return c >= 500 && c < 600; + } +} // namespace mixins + + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/iostream_support.hpp b/include/status-code/iostream_support.hpp new file mode 100644 index 000000000..f105db81d --- /dev/null +++ b/include/status-code/iostream_support.hpp @@ -0,0 +1,81 @@ +/* Proposed SG14 status_code +(C) 2018 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_IOSTREAM_SUPPORT_HPP +#define SYSTEM_ERROR2_IOSTREAM_SUPPORT_HPP + +#include "error.hpp" + +#include + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +/*! Print the status code to a `std::ostream &`. +Requires that `DomainType::value_type` implements an `operator<<` overload for `std::ostream`. +*/ +SYSTEM_ERROR2_TEMPLATE(class DomainType) // +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( +std::is_same() << std::declval::value_type>())>::type>::value)) +inline std::ostream &operator<<(std::ostream &s, const status_code &v) +{ + if(v.empty()) + { + return s << "(empty)"; + } + return s << v.domain().name().c_str() << ": " << v.value(); +} + +/*! Print a status code domain's `string_ref` to a `std::ostream &`. + */ +inline std::ostream &operator<<(std::ostream &s, const status_code_domain::string_ref &v) +{ + return s << v.c_str(); +} + +/*! Print the erased status code to a `std::ostream &`. + */ +template inline std::ostream &operator<<(std::ostream &s, const status_code> &v) +{ + if(v.empty()) + { + return s << "(empty)"; + } + return s << v.domain().name() << ": " << v.message(); +} + +/*! Print the generic code to a `std::ostream &`. + */ +inline std::ostream &operator<<(std::ostream &s, const generic_code &v) +{ + if(v.empty()) + { + return s << "(empty)"; + } + return s << v.domain().name() << ": " << v.message(); +} + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/nested_status_code.hpp b/include/status-code/nested_status_code.hpp new file mode 100644 index 000000000..afdad1dbf --- /dev/null +++ b/include/status-code/nested_status_code.hpp @@ -0,0 +1,273 @@ +/* Pointer to a SG14 status_code +(C) 2018 - 2023 Niall Douglas (5 commits) +File Created: Sep 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP +#define SYSTEM_ERROR2_NESTED_STATUS_CODE_HPP + +#include "quick_status_code_from_enum.hpp" + +#include // for allocator + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +namespace detail +{ + template class indirecting_domain : public status_code_domain + { + template friend class status_code; + using _base = status_code_domain; + + public: + struct payload_type + { + using allocator_traits = std::allocator_traits; + union + { + char _uninit[sizeof(StatusCode)]; + StatusCode sc; + }; + Allocator alloc; + + payload_type(StatusCode _sc, Allocator _alloc) + : alloc(static_cast(_alloc)) + { + allocator_traits::construct(alloc, &sc, static_cast(_sc)); + } + payload_type(const payload_type &) = delete; + payload_type(payload_type &&) = delete; + ~payload_type() { allocator_traits::destroy(alloc, &sc); } + }; + using value_type = payload_type *; + using payload_allocator_traits = typename payload_type::allocator_traits::template rebind_traits; + using _base::string_ref; + + constexpr indirecting_domain() noexcept + : _base(0xc44f7bdeb2cc50e9 ^ + typename StatusCode::domain_type().id() /* unique-ish based on domain's unique id */) + { + } + indirecting_domain(const indirecting_domain &) = default; + indirecting_domain(indirecting_domain &&) = default; // NOLINT + indirecting_domain &operator=(const indirecting_domain &) = default; + indirecting_domain &operator=(indirecting_domain &&) = default; // NOLINT + ~indirecting_domain() = default; + +#if __cplusplus < 201402L && !defined(_MSC_VER) + static inline const indirecting_domain &get() + { + static indirecting_domain v; + return v; + } +#else + static inline constexpr const indirecting_domain &get(); +#endif + + protected: + using _mycode = status_code; + + virtual int _do_name(_vtable_name_args &args) const noexcept override + { + return static_cast(typename StatusCode::domain_type())._do_name(args); + } // NOLINT + virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + return static_cast(typename StatusCode::domain_type())._do_failure(c.value()->sc); + } + virtual bool _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); + const auto &c1 = static_cast(code1); // NOLINT + return static_cast(typename StatusCode::domain_type()) + ._do_equivalent(c1.value()->sc, code2); + } + virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override + { + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + _vtable_generic_code_args args2{{}, c.value()->sc}; + static_cast(typename StatusCode::domain_type())._do_generic_code(args2); + args.ret = static_cast(args2.ret); + } + virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + _vtable_message_args args2{{}, c.value()->sc}; + const int ret = static_cast(typename StatusCode::domain_type())._do_message(args2); + args.ret = static_cast(args2.ret); + return ret; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + static_cast(typename StatusCode::domain_type())._do_throw_exception(c.value()->sc); + abort(); // suppress buggy GCC warning + } +#endif + virtual int _do_erased_copy(status_code &dst, const status_code &src, + payload_info_t dstinfo) const noexcept override + { + // Note that dst may not have its domain set + const auto srcinfo = payload_info(); + assert(src.domain() == *this); + if(dstinfo.total_size < srcinfo.total_size) + { + return ENOBUFS; + } + auto &d = static_cast<_mycode &>(dst); // NOLINT + const auto &_s = static_cast(src); // NOLINT + const payload_type &sp = *_s.value(); + typename payload_allocator_traits::template rebind_alloc payload_alloc(sp.alloc); + auto *dp = payload_allocator_traits::allocate(payload_alloc, 1); +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + try +#endif + { + payload_allocator_traits::construct(payload_alloc, dp, sp.sc, sp.alloc); + new(SYSTEM_ERROR2_ADDRESS_OF(d)) _mycode(in_place, dp); + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + catch(...) + { + payload_allocator_traits::deallocate(payload_alloc, dp, 1); + return ENOMEM; + } +#endif + return 0; + } + virtual void _do_erased_destroy(status_code &code, + payload_info_t /*unused*/) const noexcept override // NOLINT + { + assert(code.domain() == *this); + auto &c = static_cast<_mycode &>(code); // NOLINT + payload_type *p = c.value(); + typename payload_allocator_traits::template rebind_alloc payload_alloc(p->alloc); + payload_allocator_traits::destroy(payload_alloc, p); + payload_allocator_traits::deallocate(payload_alloc, p, 1); + } + }; +#if __cplusplus >= 201402L || defined(_MSC_VER) + template + constexpr indirecting_domain _indirecting_domain{}; + template + inline constexpr const indirecting_domain &indirecting_domain::get() + { + return _indirecting_domain; + } +#endif +} // namespace detail + +/*! Make an erased status code which indirects to a dynamically allocated status code, +using the allocator `alloc`. + +This is useful for shoehorning a rich status code with large value type into a small +erased status code like `system_code`, with which the status code generated by this +function is compatible. Note that this function can throw if the allocator throws. +*/ +SYSTEM_ERROR2_TEMPLATE(class T, class Alloc = std::allocator::type>) +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) // +inline status_code::type>::type>> +make_nested_status_code(T &&v, Alloc alloc = {}) +{ + using status_code_type = typename std::decay::type; + using domain_type = detail::indirecting_domain::type>; + using payload_allocator_traits = typename domain_type::payload_allocator_traits; + typename payload_allocator_traits::template rebind_alloc payload_alloc(alloc); + auto *p = payload_allocator_traits::allocate(payload_alloc, 1); +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + try +#endif + { + payload_allocator_traits::construct(payload_alloc, p, static_cast(v), static_cast(alloc)); + return status_code(in_place, p); + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + catch(...) + { + payload_allocator_traits::deallocate(payload_alloc, p, 1); + throw; + } +#endif +} + +/*! If a status code refers to a `nested_status_code` which indirects to a status +code of type `StatusCode`, return a pointer to that `StatusCode`. Otherwise return null. +*/ +SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) +inline StatusCode *get_if(status_code> *v) noexcept +{ + if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) + { + return nullptr; + } + union + { + U value; + StatusCode *ret; + }; + value = v->value(); + return ret; +} +//! \overload Const overload +SYSTEM_ERROR2_TEMPLATE(class StatusCode, class U) +SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(is_status_code::value)) +inline const StatusCode *get_if(const status_code> *v) noexcept +{ + if((0xc44f7bdeb2cc50e9 ^ typename StatusCode::domain_type().id()) != v->domain().id()) + { + return nullptr; + } + union + { + U value; + const StatusCode *ret; + }; + value = v->value(); + return ret; +} + +/*! If a status code refers to a `nested_status_code`, return the id of the erased +status code's domain. Otherwise return a meaningless number. +*/ +template +inline typename status_code_domain::unique_id_type get_id(const status_code> &v) noexcept +{ + return 0xc44f7bdeb2cc50e9 ^ v.domain().id(); +} + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/nt_code.hpp b/include/status-code/nt_code.hpp new file mode 100644 index 000000000..20e423092 --- /dev/null +++ b/include/status-code/nt_code.hpp @@ -0,0 +1,259 @@ +/* Proposed SG14 status_code +(C) 2018 - 2026 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_NT_CODE_HPP +#define SYSTEM_ERROR2_NT_CODE_HPP + +#if !defined(_WIN32) && !defined(STANDARDESE_IS_IN_THE_HOUSE) +#error This file should only be included on Windows +#endif + +#include "win32_code.hpp" + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +//! \exclude +namespace win32 +{ +#ifdef __MINGW32__ + extern "C" + { +#endif + // A Win32 NTSTATUS + using NTSTATUS = long; + // A Win32 HMODULE + using HMODULE = void *; + // Used to retrieve where the NTDLL DLL is mapped into memory + extern HMODULE __stdcall GetModuleHandleW(const wchar_t *lpModuleName); +#ifdef __MINGW32__ + } +#else +#pragma comment(lib, "kernel32.lib") +#if(defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) +#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPEAXPEB_W@Z=GetModuleHandleW") +#elif defined(__x86__) || defined(_M_IX86) || defined(__i386__) +#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YGPAXPB_W@Z=_GetModuleHandleW@4") +#elif defined(__arm__) || defined(_M_ARM) +#pragma comment(linker, "/alternatename:?GetModuleHandleW@win32@system_error2@@YAPAXPB_W@Z=GetModuleHandleW") +#else +#error Unknown architecture +#endif +#endif +} // namespace win32 + +class _nt_code_domain; +//! (Windows only) A NT error code, those returned by NT kernel functions. +using nt_code = status_code<_nt_code_domain>; +//! (Windows only) A specialisation of `status_error` for the NT error code domain. +using nt_error = status_error<_nt_code_domain>; + +/*! (Windows only) The implementation of the domain for NT error codes, those returned by NT kernel functions. + */ +class _nt_code_domain : public status_code_domain +{ + template friend class status_code; + friend class _com_code_domain; + using _base = status_code_domain; + static int _nt_code_to_errno(win32::NTSTATUS c) + { + if(c >= 0) + { + return 0; // success + } + switch(static_cast(c)) + { +#include "detail/nt_code_to_generic_code.ipp" + } + return -1; + } + static win32::DWORD _nt_code_to_win32_code(win32::NTSTATUS c) // NOLINT + { + if(c >= 0) + { + return 0; // success + } + switch(static_cast(c)) + { +#include "detail/nt_code_to_win32_code.ipp" + } + return static_cast(-1); + } + //! Construct from a NT error code + static _base::string_ref _make_string_ref(int &errcode, win32::NTSTATUS c) noexcept + { + wchar_t buffer[32768]; + static win32::HMODULE ntdll = win32::GetModuleHandleW(L"NTDLL.DLL"); + win32::DWORD wlen = + win32::FormatMessageW(0x00000800 /*FORMAT_MESSAGE_FROM_HMODULE*/ | 0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | + 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, + ntdll, c, (1 << 10) /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/, buffer, 32768, nullptr); + size_t allocation = wlen + (wlen >> 1); + win32::DWORD bytes; + if(wlen == 0) + { + errcode = ENOENT; + return _base::string_ref("failed to get message from system"); + } + for(;;) + { + auto *p = static_cast(malloc(allocation)); // NOLINT + if(p == nullptr) + { + errcode = ENOMEM; + return _base::string_ref("failed to get message from system"); + } + bytes = + win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, buffer, (int) (wlen + 1), p, (int) allocation, nullptr, nullptr); + if(bytes != 0) + { + char *end = strchr(p, 0); + while(end[-1] == 10 || end[-1] == 13) + { + --end; + } + *end = 0; // NOLINT + _base::atomic_refcounted_string_ref ret(p, end - p); + free(p); + return ret; + } + free(p); // NOLINT + if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/) + { + allocation += allocation >> 2; + continue; + } + errcode = EILSEQ; + return _base::string_ref("failed to get message from system"); + } + } + +public: + //! The value type of the NT code, which is a `win32::NTSTATUS` + using value_type = win32::NTSTATUS; + using _base::string_ref; + +public: + //! Default constructor + constexpr explicit _nt_code_domain(typename _base::unique_id_type id = 0x93f3b4487e4af25b) noexcept + : _base(id) + { + } + _nt_code_domain(const _nt_code_domain &) = default; + _nt_code_domain(_nt_code_domain &&) = default; + _nt_code_domain &operator=(const _nt_code_domain &) = default; + _nt_code_domain &operator=(_nt_code_domain &&) = default; + ~_nt_code_domain() = default; + + //! Constexpr singleton getter. Returns the constexpr nt_code_domain variable. + static inline constexpr const _nt_code_domain &get(); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref("NT domain"); + return 0; + } // NOLINT + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); + return static_cast(code).value() < 0; // NOLINT + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool + _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); + const auto &c1 = static_cast(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + return c1.value() == c2.value(); + } + if(code2.domain() == generic_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(static_cast(c2.value()) == _nt_code_to_errno(c1.value())) + { + return true; + } + } + if(code2.domain() == win32_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(c2.value() == _nt_code_to_win32_code(c1.value())) + { + return true; + } + } + return false; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override + { + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + args.ret = generic_code(static_cast(_nt_code_to_errno(c.value()))); + } + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); + const auto &c = static_cast(args.code); // NOLINT + int ret = 0; + args.ret = _make_string_ref(ret, c.value()); + return ret; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); + const auto &c = static_cast(code); // NOLINT + throw status_error<_nt_code_domain>(c); + } +#endif +}; +//! (Windows only) A constexpr source variable for the NT code domain, which is that of NT kernel functions. Returned by +//! `_nt_code_domain::get()`. +constexpr _nt_code_domain nt_code_domain; +inline constexpr const _nt_code_domain &_nt_code_domain::get() +{ + return nt_code_domain; +} + +SYSTEM_ERROR2_NAMESPACE_END + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + +#endif diff --git a/include/status-code/posix_code.hpp b/include/status-code/posix_code.hpp new file mode 100644 index 000000000..4e86d6df6 --- /dev/null +++ b/include/status-code/posix_code.hpp @@ -0,0 +1,211 @@ +/* Proposed SG14 status_code +(C) 2018-2020 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_POSIX_CODE_HPP +#define SYSTEM_ERROR2_POSIX_CODE_HPP + +#ifdef SYSTEM_ERROR2_NOT_POSIX +#error is not includable when SYSTEM_ERROR2_NOT_POSIX is defined! +#endif + +#include "quick_status_code_from_enum.hpp" + +#include // for strchr and strerror_r + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning(disable : 6326) // constant comparison +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +// Fix for issue #48 Issue compiling on arm-none-eabi (newlib) with GNU extensions off +#if !defined(_MSC_VER) && !defined(__APPLE__) +namespace detail +{ + namespace avoid_string_include + { +#if defined(__ANDROID__) + using ::strerror_r; +#elif defined(__GLIBC__) && !defined(__UCLIBC__) + // This returns int for non-glibc strerror_r, but glibc's is particularly weird so we retain it + extern "C" char *strerror_r(int errnum, char *buf, size_t buflen); +#else + extern "C" int strerror_r(int errnum, char *buf, size_t buflen); +#endif + } // namespace avoid_string_include +} // namespace detail +#endif + +class _posix_code_domain; +//! A POSIX error code, those returned by `errno`. +using posix_code = status_code<_posix_code_domain>; +//! A specialisation of `status_error` for the POSIX error code domain. +using posix_error = status_error<_posix_code_domain>; + +namespace mixins +{ + template struct mixin : public Base + { + using Base::Base; + + //! Returns a `posix_code` for the current value of `errno`. + static posix_code current() noexcept; + }; +} // namespace mixins + +/*! The implementation of the domain for POSIX error codes, those returned by `errno`. + */ +class _posix_code_domain : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + + static _base::string_ref _make_string_ref(int &errcode, int c) noexcept + { + char buffer[1024] = ""; + errno = 0; +#ifdef _WIN32 + strerror_s(buffer, sizeof(buffer), c); +#elif defined(__GLIBC__) && !defined(__UCLIBC__) // handle glibc's weird strerror_r() + char *s = detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer)); // NOLINT + if(s != nullptr) + { + strncpy(buffer, s, sizeof(buffer) - 1); // NOLINT + buffer[1023] = 0; + } +#elif !defined(__APPLE__) + detail::avoid_string_include::strerror_r(c, buffer, sizeof(buffer)); +#else + strerror_r(c, buffer, sizeof(buffer)); +#endif + errcode = errno; + return _base::atomic_refcounted_string_ref(buffer); + } + +public: + //! The value type of the POSIX code, which is an `int` + using value_type = int; + using _base::string_ref; + + //! Default constructor + constexpr explicit _posix_code_domain(typename _base::unique_id_type id = 0xa59a56fe5f310933) noexcept + : _base(id) + { + } + _posix_code_domain(const _posix_code_domain &) = default; + _posix_code_domain(_posix_code_domain &&) = default; + _posix_code_domain &operator=(const _posix_code_domain &) = default; + _posix_code_domain &operator=(_posix_code_domain &&) = default; + ~_posix_code_domain() = default; + + //! Constexpr singleton getter. Returns constexpr posix_code_domain variable. + static inline constexpr const _posix_code_domain &get(); + +protected: + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref("posix domain"); + return 0; + } + + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + + SYSTEM_ERROR2_CONSTEXPR20 virtual bool _do_failure(const status_code &code) const noexcept override // NOLINT + { + assert(code.domain() == *this); // NOLINT + return static_cast(code).value() != 0; // NOLINT + } + SYSTEM_ERROR2_CONSTEXPR20 virtual bool + _do_equivalent(const status_code &code1, + const status_code &code2) const noexcept override // NOLINT + { + assert(code1.domain() == *this); // NOLINT + const auto &c1 = static_cast(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast(code2); // NOLINT + return c1.value() == c2.value(); + } + if(code2.domain() == generic_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + if(static_cast(c2.value()) == c1.value()) + { + return true; + } + } + return false; + } + virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + args.ret = generic_code(static_cast(c.value())); + } + virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto &c = static_cast(args.code); // NOLINT + int errcode = 0; + args.ret = _make_string_ref(errcode, c.value()); + return errcode; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override // NOLINT + { + assert(code.domain() == *this); // NOLINT + const auto &c = static_cast(code); // NOLINT + throw status_error<_posix_code_domain>(c); + } +#endif +}; +//! A constexpr source variable for the POSIX code domain, which is that of `errno`. Returned by +//! `_posix_code_domain::get()`. +constexpr _posix_code_domain posix_code_domain; +inline constexpr const _posix_code_domain &_posix_code_domain::get() +{ + return posix_code_domain; +} + +namespace mixins +{ + template inline posix_code mixin::current() noexcept + { + return posix_code(errno); + } +} // namespace mixins + +SYSTEM_ERROR2_NAMESPACE_END + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif + +#endif diff --git a/include/status-code/quick_status_code_from_enum.hpp b/include/status-code/quick_status_code_from_enum.hpp new file mode 100644 index 000000000..6d974b3fd --- /dev/null +++ b/include/status-code/quick_status_code_from_enum.hpp @@ -0,0 +1,240 @@ +/* Proposed SG14 status_code +(C) 2018 - 2020 Niall Douglas (5 commits) +File Created: May 2020 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP +#define SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP + +#ifndef SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES +#define SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES 1 +#endif + +#include "generic_code.hpp" + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +template class _quick_status_code_from_enum_domain; +//! A status code wrapping `Enum` generated from `quick_status_code_from_enum`. +template using quick_status_code_from_enum_code = status_code<_quick_status_code_from_enum_domain>; + +//! Defaults for an implementation of `quick_status_code_from_enum` +template struct quick_status_code_from_enum_defaults +{ + //! The type of the resulting code + using code_type = quick_status_code_from_enum_code; + //! Used within `quick_status_code_from_enum` to define a mapping of enumeration value with its status code + struct mapping + { + //! The enumeration type + using enumeration_type = Enum; + + //! The value being mapped + const Enum value; + //! A string representation for this enumeration value + const char *message; + //! A list of `errc` equivalents for this enumeration value + const std::initializer_list code_mappings; + }; + //! Used within `quick_status_code_from_enum` to define mixins for the status code wrapping `Enum` + template struct mixin : Base + { + using Base::Base; + }; +}; + +/*! The implementation of the domain for status codes wrapping `Enum` generated from `quick_status_code_from_enum`. + */ +template class _quick_status_code_from_enum_domain : public status_code_domain +{ + template friend class status_code; + using _base = status_code_domain; + using _src = quick_status_code_from_enum; + +public: + //! The value type of the quick status code from enum + using value_type = Enum; + using _base::string_ref; + + constexpr _quick_status_code_from_enum_domain() + : status_code_domain(_src::domain_uuid, _uuid_size()) + { + } + _quick_status_code_from_enum_domain(const _quick_status_code_from_enum_domain &) = default; + _quick_status_code_from_enum_domain(_quick_status_code_from_enum_domain &&) = default; + _quick_status_code_from_enum_domain &operator=(const _quick_status_code_from_enum_domain &) = default; + _quick_status_code_from_enum_domain &operator=(_quick_status_code_from_enum_domain &&) = default; + ~_quick_status_code_from_enum_domain() = default; + +#if __cplusplus < 201402L && !defined(_MSC_VER) + static inline const _quick_status_code_from_enum_domain &get() + { + static _quick_status_code_from_enum_domain v; + return v; + } +#else + static inline constexpr const _quick_status_code_from_enum_domain &get(); +#endif + +protected: + // Not sure if a hash table is worth it here, most enumerations won't be long enough to be worth it + // Also, until C++ 20's consteval, the hash table would get emitted into the binary, bloating it + static SYSTEM_ERROR2_CONSTEXPR14 const typename _src::mapping *_find_mapping(value_type v) noexcept + { + for(const auto &i : _src::value_mappings()) + { + if(i.value == v) + { + return &i; + } + } + return nullptr; + } + + SYSTEM_ERROR2_CONSTEXPR20 virtual int _do_name(_vtable_name_args &args) const noexcept override + { + args.ret = string_ref(_src::domain_name); + return 0; + } + SYSTEM_ERROR2_CONSTEXPR20 virtual void _do_payload_info(_vtable_payload_info_args &args) const noexcept override + { + args.ret = {sizeof(value_type), sizeof(status_code_domain *) + sizeof(value_type), + (alignof(value_type) > alignof(status_code_domain *)) ? alignof(value_type) : + alignof(status_code_domain *)}; + } + + virtual bool _do_failure(const status_code &code) const noexcept override + { + assert(code.domain() == *this); // NOLINT + // If `errc::success` is in the generic code mapping, it is not a failure + const auto *mapping = + _find_mapping(static_cast &>(code).value()); +#if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif + if(mapping != nullptr) + { + for(errc ec : mapping->code_mappings) + { + if(ec == errc::success) + { + return false; + } + } + } + return true; + } + virtual bool _do_equivalent(const status_code &code1, const status_code &code2) const noexcept override + { + assert(code1.domain() == *this); // NOLINT + const auto &c1 = static_cast &>(code1); // NOLINT + if(code2.domain() == *this) + { + const auto &c2 = static_cast &>(code2); // NOLINT + return c1.value() == c2.value(); + } + if(code2.domain() == generic_code_domain) + { + const auto &c2 = static_cast(code2); // NOLINT + const auto *mapping = _find_mapping(c1.value()); +#if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif + if(mapping != nullptr) + { + for(errc ec : mapping->code_mappings) + { + if(ec == c2.value()) + { + return true; + } + } + } + } + return false; + } + virtual void _do_generic_code(_vtable_generic_code_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto *mapping = + _find_mapping(static_cast &>(args.code).value()); +#if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif + if(mapping != nullptr) + { + if(mapping->code_mappings.size() > 0) + { + args.ret = *mapping->code_mappings.begin(); + return; + } + } + args.ret = errc::unknown; + } + virtual int _do_message(_vtable_message_args &args) const noexcept override + { + assert(args.code.domain() == *this); // NOLINT + const auto *mapping = + _find_mapping(static_cast &>(args.code).value()); +#if SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_ASSERT_ON_MISSING_MAPPING_TABLE_ENTRIES + assert(mapping != nullptr); // if this fires, you forgot to add the enum to the mapping table +#endif + if(mapping != nullptr) + { + args.ret = string_ref(mapping->message); + return 0; + } + args.ret = string_ref("unknown"); + return 0; + } +#if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(STANDARDESE_IS_IN_THE_HOUSE) + SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code &code) const override + { + assert(code.domain() == *this); // NOLINT + const auto &c = static_cast &>(code); // NOLINT + throw status_error<_quick_status_code_from_enum_domain>(c); + } +#endif +}; + +#if __cplusplus >= 201402L || defined(_MSC_VER) +template constexpr _quick_status_code_from_enum_domain quick_status_code_from_enum_domain = {}; +template +inline constexpr const _quick_status_code_from_enum_domain &_quick_status_code_from_enum_domain::get() +{ + return quick_status_code_from_enum_domain; +} +#endif + +namespace mixins +{ + template + struct mixin> + : public quick_status_code_from_enum::template mixin + { + using quick_status_code_from_enum::template mixin::mixin; + }; +} // namespace mixins + +SYSTEM_ERROR2_NAMESPACE_END + +#endif diff --git a/include/status-code/result.hpp b/include/status-code/result.hpp new file mode 100644 index 000000000..c55befd18 --- /dev/null +++ b/include/status-code/result.hpp @@ -0,0 +1,413 @@ +/* A partial result based on std::variant and proposed std::error +(C) 2020-2026 Niall Douglas (11 commits) +File Created: Jan 2020 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_RESULT_HPP +#define SYSTEM_ERROR2_RESULT_HPP + +#include "error.hpp" + +#if __cplusplus >= 201703L || _HAS_CXX17 +#if __has_include() + +#include +#include + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +template inline constexpr std::in_place_type_t in_place_type{}; + +template class result; + +//! \brief A trait for detecting result types +template struct is_result : public std::false_type +{ +}; +template struct is_result> : public std::true_type +{ +}; + +/*! \brief Exception type representing the failure to retrieve an error. + */ +class bad_result_access : public std::exception +{ +public: + bad_result_access() = default; + //! Return an explanatory string + virtual const char *what() const noexcept override { return "bad result access"; } // NOLINT +}; + +namespace detail +{ + struct void_ + { + }; + template using devoid = std::conditional_t, void_, T>; +} // namespace detail + +/*! \class result +\brief A imperfect `result` type with its error type hardcoded to `error`, only available on C++ 17 or later. + +Note that the proper `result` type does not have the possibility of +valueless by exception state. This implementation is therefore imperfect. +*/ +template class result : protected std::variant> +{ + using _base = std::variant>; + static_assert(!std::is_reference_v, "Type cannot be a reference"); + static_assert(!std::is_array_v, "Type cannot be an array"); + static_assert(!std::is_same_v, "Type cannot be a std::error"); + // not success nor failure types + + struct _implicit_converting_constructor_tag + { + }; + struct _explicit_converting_constructor_tag + { + }; + struct _implicit_constructor_tag + { + }; + struct _implicit_in_place_value_constructor_tag + { + }; + struct _implicit_in_place_error_constructor_tag + { + }; + +public: + //! The value type + using value_type = T; + //! The error type + using error_type = SYSTEM_ERROR2_NAMESPACE::error; + //! The value type, if it is available, else a usefully named unusable internal type + using value_type_if_enabled = detail::devoid; + //! Used to rebind result types + template using rebind = result; + +protected: + constexpr void _check() const + { + if(_base::index() == 0) + { + std::get_if<0>(this)->throw_exception(); + } + } + constexpr +#ifdef _MSC_VER + __declspec(noreturn) +#elif defined(__GNUC__) || defined(__clang__) + __attribute__((noreturn)) +#endif + void _ub() + { + assert(false); // NOLINT +#if defined(__GNUC__) || defined(__clang__) + __builtin_unreachable(); +#elif defined(_MSC_VER) + __assume(0); +#endif + } + +public: + constexpr _base &_internal() noexcept { return *this; } + constexpr const _base &_internal() const noexcept { return *this; } + + //! Default constructor is disabled + result() = delete; + //! Copy constructor + result(const result &) = delete; + //! Move constructor + result(result &&) = default; + //! Copy assignment + result &operator=(const result &) = delete; + //! Move assignment + result &operator=(result &&) = default; + //! Destructor + ~result() = default; + + //! Implicit result converting move constructor + SYSTEM_ERROR2_TEMPLATE(class U) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v)) + constexpr result(result &&o, + _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) + : _base(std::move(o)) + { + } + //! Implicit result converting copy constructor + SYSTEM_ERROR2_TEMPLATE(class U) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_convertible_v)) + constexpr result(const result &o, + _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) + : _base(o) + { + } + //! Explicit result converting move constructor + SYSTEM_ERROR2_TEMPLATE(class U) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v)) + constexpr explicit result(result &&o, + _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) + : _base(std::move(o)) + { + } + //! Explicit result converting copy constructor + SYSTEM_ERROR2_TEMPLATE(class U) + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(std::is_constructible_v)) + constexpr explicit result(const result &o, + _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v) + : _base(o) + { + } + + //! Anything which `std::variant` will construct from, we shall implicitly construct from + using _base::_base; + + //! Special case `in_place_type_t` + constexpr explicit result(std::in_place_type_t /*unused*/) noexcept + : _base(in_place_type) + { + } + + //! Implicit in-place converting error constructor + SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, long = 5) // + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v && + std::is_constructible_v) // + &&std::is_constructible_v)) + constexpr result(Arg1 &&arg1, Arg2 &&arg2, + Args &&...args) noexcept(std::is_nothrow_constructible_v) + : _base(std::in_place_index<0>, std::forward(arg1), std::forward(arg2), std::forward(args)...) + { + } + + //! Implicit in-place converting value constructor + SYSTEM_ERROR2_TEMPLATE(class Arg1, class Arg2, class... Args, int = 5) // + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(!(std::is_constructible_v && + std::is_constructible_v) // + &&std::is_constructible_v)) + constexpr result(Arg1 &&arg1, Arg2 &&arg2, + Args &&...args) noexcept(std::is_nothrow_constructible_v) + : _base(std::in_place_index<1>, std::forward(arg1), std::forward(arg2), std::forward(args)...) + { + } + + //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a + //! `status_code`. + SYSTEM_ERROR2_TEMPLATE(class U, class... Args, // + class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result< + U, Args...>::type) // Safe ADL lookup of make_status_code(), returns void if not found + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED( + !std::is_same::type, result>::value // not copy/move of self + && !std::is_same::type, value_type>::value // not copy/move of value type + && is_status_code::value // ADL makes a status code + && std::is_constructible::value)) // ADLed status code is compatible + constexpr result(U &&v, Args &&...args) noexcept(noexcept(make_status_code(std::declval(), + std::declval()...))) // NOLINT + : _base(std::in_place_index<0>, make_status_code(static_cast(v), static_cast(args)...)) + { + } + + //! Swap with another result + constexpr void swap(result &o) noexcept(std::is_nothrow_swappable_v<_base>) { _base::swap(o); } + + //! Clone the result + constexpr result clone() const { return has_value() ? result(value()) : result(error().clone()); } + + //! True if result has a value + constexpr bool has_value() const noexcept { return _base::index() == 1; } + //! True if result has a value + explicit operator bool() const noexcept { return has_value(); } + //! True if result has an error + constexpr bool has_error() const noexcept { return _base::index() == 0; } + + //! Accesses the value if one exists, else calls `.error().throw_exception()`. + constexpr value_type_if_enabled &value() & + { + _check(); + return std::get<1>(*this); + } + //! Accesses the value if one exists, else calls `.error().throw_exception()`. + constexpr const value_type_if_enabled &value() const & + { + _check(); + return std::get<1>(*this); + } + //! Accesses the value if one exists, else calls `.error().throw_exception()`. + constexpr value_type_if_enabled &&value() && + { + _check(); + return std::get<1>(std::move(*this)); + } + //! Accesses the value if one exists, else calls `.error().throw_exception()`. + constexpr const value_type_if_enabled &&value() const && + { + _check(); + return std::get<1>(std::move(*this)); + } + + //! Accesses the error if one exists, else throws `bad_result_access`. + constexpr error_type &error() & + { + if(!has_error()) + { +#ifdef __cpp_exceptions + throw bad_result_access(); +#else + abort(); +#endif + } + return *std::get_if<0>(this); + } + //! Accesses the error if one exists, else throws `bad_result_access`. + constexpr const error_type &error() const & + { + if(!has_error()) + { +#ifdef __cpp_exceptions + throw bad_result_access(); +#else + abort(); +#endif + } + return *std::get_if<0>(this); + } + //! Accesses the error if one exists, else throws `bad_result_access`. + constexpr error_type &&error() && + { + if(!has_error()) + { +#ifdef __cpp_exceptions + throw bad_result_access(); +#else + abort(); +#endif + } + return std::move(*std::get_if<0>(this)); + } + //! Accesses the error if one exists, else throws `bad_result_access`. + constexpr const error_type &&error() const && + { + if(!has_error()) + { +#ifdef __cpp_exceptions + throw bad_result_access(); +#else + abort(); +#endif + } + return std::move(*std::get_if<0>(this)); + } + + //! Accesses the value, being UB if none exists + constexpr value_type_if_enabled &assume_value() & noexcept + { + if(!has_value()) + { + _ub(); + } + return *std::get_if<1>(this); + } + //! Accesses the error, being UB if none exists + constexpr const value_type_if_enabled &assume_value() const & noexcept + { + if(!has_value()) + { + _ub(); + } + return *std::get_if<1>(this); + } + //! Accesses the error, being UB if none exists + constexpr value_type_if_enabled &&assume_value() && noexcept + { + if(!has_value()) + { + _ub(); + } + return std::move(*std::get_if<1>(this)); + } + //! Accesses the error, being UB if none exists + constexpr const value_type_if_enabled &&assume_value() const && noexcept + { + if(!has_value()) + { + _ub(); + } + return std::move(*std::get_if<1>(this)); + } + + //! Accesses the error, being UB if none exists + constexpr error_type &assume_error() & noexcept + { + if(!has_error()) + { + _ub(); + } + return *std::get_if<0>(this); + } + //! Accesses the error, being UB if none exists + constexpr const error_type &assume_error() const & noexcept + { + if(!has_error()) + { + _ub(); + } + return *std::get_if<0>(this); + } + //! Accesses the error, being UB if none exists + constexpr error_type &&assume_error() && noexcept + { + if(!has_error()) + { + _ub(); + } + return std::move(*std::get_if<0>(this)); + } + //! Accesses the error, being UB if none exists + constexpr const error_type &&assume_error() const && noexcept + { + if(!has_error()) + { + _ub(); + } + return std::move(*std::get_if<0>(this)); + } +}; + +//! True if the two results compare equal. +template () == std::declval())> +constexpr inline bool operator==(const result &a, const result &b) noexcept +{ + const auto &x = a._internal(); + return x == b; +} +//! True if the two results compare unequal. +template () != std::declval())> +constexpr inline bool operator!=(const result &a, const result &b) noexcept +{ + const auto &x = a._internal(); + return x != b; +} + +SYSTEM_ERROR2_NAMESPACE_END + +#endif +#endif +#endif diff --git a/include/status-code/status_code.hpp b/include/status-code/status_code.hpp new file mode 100644 index 000000000..fd8ac4450 --- /dev/null +++ b/include/status-code/status_code.hpp @@ -0,0 +1,900 @@ +/* Proposed SG14 status_code +(C) 2018 - 2026 Niall Douglas (5 commits) +File Created: Feb 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file Licence.txt or copy at +http://www.boost.org/LICENSE_1_0.txt) +*/ + +#ifndef SYSTEM_ERROR2_STATUS_CODE_HPP +#define SYSTEM_ERROR2_STATUS_CODE_HPP + +#include "status_code_domain.hpp" + +#if (__cplusplus >= 201700 || _HAS_CXX17) && !defined(SYSTEM_ERROR2_DISABLE_STD_IN_PLACE) +// 0.26 +#include // for in_place + +SYSTEM_ERROR2_NAMESPACE_BEGIN +using in_place_t = std::in_place_t; +using std::in_place; +SYSTEM_ERROR2_NAMESPACE_END + +#else + +SYSTEM_ERROR2_NAMESPACE_BEGIN +//! Aliases `std::in_place_t` if on C++ 17 or later, else defined locally. +struct in_place_t +{ + explicit in_place_t() = default; +}; +//! Aliases `std::in_place` if on C++ 17 or later, else defined locally. +constexpr in_place_t in_place{}; +SYSTEM_ERROR2_NAMESPACE_END +#endif + +SYSTEM_ERROR2_NAMESPACE_BEGIN + +template class errored_status_code; + +//! Namespace for user injected mixins +namespace mixins +{ + template struct mixin : public Base + { + using Base::Base; + }; +} // namespace mixins + +namespace detail +{ + SYSTEM_ERROR2_TEMPLATE(class ErasedType) // + SYSTEM_ERROR2_TREQUIRES(SYSTEM_ERROR2_TPRED(traits::is_move_bitcopying::value)) + struct erased + { + using value_type = ErasedType; + }; +} // namespace detail + +/*! The tag type used to specialise erased editions of `status_code`. +Available only if `ErasedType` satisfies `traits::is_move_bitcopying::value`. +*/ +template // +using status_code_erased_tag_type = detail::erased; + +/*! Specialise this template to quickly wrap a third party enumeration into a +custom status code domain. + +Use like this: + +```c++ +SYSTEM_ERROR2_NAMESPACE_BEGIN +template <> struct quick_status_code_from_enum : quick_status_code_from_enum_defaults +{ + // Text name of the enum + static constexpr const auto domain_name = "Another Code"; + // Unique UUID for the enum. PLEASE use https://www.random.org/cgi-bin/randbyte?nbytes=16&format=h + static constexpr const auto domain_uuid = "{be201f65-3962-dd0e-1266-a72e63776a42}"; + // Map of each enum value to its text string, and list of semantically equivalent errc's + static const std::initializer_list &value_mappings() + { + static const std::initializer_list> v = { + // Format is: { enum value, "string representation", { list of errc mappings ... } } + {AnotherCode::success1, "Success 1", {errc::success}}, // + {AnotherCode::goaway, "Go away", {errc::permission_denied}}, // + {AnotherCode::success2, "Success 2", {errc::success}}, // + {AnotherCode::error2, "Error 2", {}}, // + }; + return v; + } + // Completely optional definition of mixin for the status code synthesised from `Enum`. It can be omitted. + template struct mixin : Base + { + using Base::Base; + constexpr int custom_method() const { return 42; } + }; +}; +SYSTEM_ERROR2_NAMESPACE_END +``` + +Note that if the `errc` mapping contains `errc::success`, then +the enumeration value is considered to be a successful value. +Otherwise it is considered to be a failure value. + +The first value in the `errc` mapping is the one chosen as the +`generic_code` conversion. Other values are used during equivalence +comparisons. +*/ +template struct quick_status_code_from_enum; + +namespace detail +{ + template struct is_status_code + { + static constexpr bool value = false; + }; + template struct is_status_code> + { + static constexpr bool value = true; + }; + template struct is_status_code> + { + static constexpr bool value = true; + }; + template struct is_errored_status_code + { + static constexpr bool value = false; + }; + template struct is_errored_status_code> + { + static constexpr bool value = true; + }; + + template struct is_erased_status_code + { + static constexpr bool value = false; + }; + template struct is_erased_status_code>> + { + static constexpr bool value = true; + }; + template struct is_erased_status_code>> + { + static constexpr bool value = true; + }; + template struct is_erased_errored_status_code + { + static constexpr bool value = false; + }; + template struct is_erased_errored_status_code>> + { + static constexpr bool value = true; + }; + +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ >= 8 + // From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf + namespace impl + { + template struct make_void + { + using type = void; + }; + template using void_t = typename make_void::type; + template struct types + { + using type = types; + }; + template