diff options
author | Vittorio Romeo <vittorio.romeo@outlook.com> | 2023-04-07 01:38:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-07 01:38:55 +0200 |
commit | f42c0404c3355b89e17986096ab2186a174535af (patch) | |
tree | 45adf7f16e14bdcfce81e22df5da8bfbf441cd72 | |
parent | 0ff4f62bf3191ce679f69849cb7743175f81498c (diff) | |
parent | 0f3601c0c14e4a438b9d444f78462c8e8901ef4b (diff) |
Merge pull request #408 from vittorioromeo/develop
Open Hexagon v2.1.7
98 files changed, 10562 insertions, 6766 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci_linux.yml index eaca082c..15eaf0f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci_linux.yml @@ -1,4 +1,4 @@ -name: CI +name: CI Linux # Trigger this workflow on push or pull request on: [push, pull_request] @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v1 with: submodules: recursive - + - name: Install dependencies run: | sudo apt-get update @@ -33,19 +33,13 @@ jobs: cd build export LD_RUN_PATH="/opt/gcc-latest/lib64" cmake -GNinja -DCMAKE_BUILD_TYPE=RELEASE -DSSVOH_HEADLESS_TESTS=1 .. - + - name: Build run: ninja -C build - name: Copy artifacts run: | cp build/SSVOpenHexagon build/OHWorkshopUploader _RELEASE - cp build/_deps/sfml-build/lib/libsfml-*.so _RELEASE - mv _RELEASE/libsfml-audio.so _RELEASE/libsfml-audio.so.2.5 - mv _RELEASE/libsfml-graphics.so _RELEASE/libsfml-graphics.so.2.5 - mv _RELEASE/libsfml-network.so _RELEASE/libsfml-network.so.2.5 - mv _RELEASE/libsfml-system.so _RELEASE/libsfml-system.so.2.5 - mv _RELEASE/libsfml-window.so _RELEASE/libsfml-window.so.2.5 - name: Upload artifacts uses: actions/upload-artifact@v1 @@ -60,7 +54,7 @@ jobs: cp -R _RELEASE/Packs build cp -R _RELEASE/Packs build/test ninja -C build check - + - name: Check ldd run: | ldd build/test/test.Replay.t diff --git a/.github/workflows/ci_windows.yml b/.github/workflows/ci_windows.yml new file mode 100644 index 00000000..b35590aa --- /dev/null +++ b/.github/workflows/ci_windows.yml @@ -0,0 +1,60 @@ +name: CI Windows + +# Trigger this workflow on push or pull request +on: [push, pull_request] + +jobs: + build: + name: Build and test (windows) + + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + + steps: + - name: Install dependencies + uses: msys2/setup-msys2@v2 + with: + install: >- + git + base-devel + mingw-w64-x86_64-gcc + mingw-w64-x86_64-cmake + mingw-w64-x86_64-openal + update: true + + - uses: actions/checkout@v1 + with: + submodules: recursive + + - name: CMake configure + run: | + cd build + cmake -GNinja -DCMAKE_BUILD_TYPE=RELEASE -DSSVOH_HEADLESS_TESTS=1 .. + + - name: Build + run: ninja -C build + + - name: Copy artifacts + run: | + cp build/SSVOpenHexagon*.exe build/OHWorkshopUploader.exe _RELEASE + cp build/_deps/zlib-build/libzlib.dll _RELEASE + cp /mingw64/bin/libwinpthread-1.dll _RELEASE + cp /mingw64/bin/libstdc++-6.dll _RELEASE + cp /mingw64/bin/libgcc_s_seh-1.dll _RELEASE + cp /mingw64/bin/libopenal-1.dll _RELEASE + cp _RELEASE/*.dll build/test + + - name: Upload artifacts + uses: actions/upload-artifact@v1 + with: + name: OpenHexagon-Windows + path: _RELEASE + + - name: Run tests + run: | + mkdir -p build/test + cp -R _RELEASE/Packs build/test + ninja -C build check diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f98c9f6..06250245 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 3.9) +# TODO: build timestamp +# string(TIMESTAMP CMAKE_TIMESTAMP) +# message(${CMAKE_TIMESTAMP}) + # # # ----------------------------------------------------------------------------- @@ -97,13 +101,14 @@ include(${CPM_DOWNLOAD_LOCATION}) # CPM: SFML # ----------------------------------------------------------------------------- -set(BUILD_SHARED_LIBS true) -set(SFML_STATIC_LIBRARIES false) +set(BUILD_SHARED_LIBS false) +set(SFML_STATIC_LIBRARIES true) +set(SFML_ENABLE_PCH true) CPMAddPackage( NAME SFML GITHUB_REPOSITORY vittorioromeo/SFML - GIT_TAG 13860e9fe428aaccaaef958d748d1c42083d1a3c + GIT_TAG dacceddbd1adee49c7bfd120266e200800550394 ) set_target_properties(sfml-system PROPERTIES UNITY_BUILD ON) @@ -125,7 +130,7 @@ set(LUAJIT_DISABLE_FFI ON CACHE BOOL "" FORCE) CPMAddPackage( NAME luajit GIT_REPOSITORY https://github.com/vittorioromeo/LuaJIT - GIT_TAG fa8feeb9580f233a397fb484d84c2108e26b2a00 + GIT_TAG 4dc5ffbf60b16e54274c470fcc022165685c96fe ) set(LUAJIT_DISABLE_FFI true) @@ -227,7 +232,7 @@ set(SODIUM_DISABLE_TESTS ON CACHE BOOL "" FORCE) CPMAddPackage( NAME boostpfr GITHUB_REPOSITORY boostorg/pfr - GIT_TAG ed28602c2cc7b330140e67412552ed7ec2e4fe8d + GIT_TAG b0bf18798c7037ca8a91a1cd2ad2e5798d8f6d46 DOWNLOAD_ONLY YES ) @@ -286,7 +291,7 @@ message(STATUS "initialized project ${PROJECT_NAME}") # Global unity builds # ----------------------------------------------------------------------------- -set(CMAKE_UNITY_BUILD ON) +set(CMAKE_UNITY_BUILD OFF) set(CMAKE_UNITY_BUILD_BATCH_SIZE 4) # diff --git a/_RELEASE/config.json b/_RELEASE/config.json index 23b1dd8f..dbd20441 100644 --- a/_RELEASE/config.json +++ b/_RELEASE/config.json @@ -12,6 +12,7 @@ "camera_shake_multiplier" : 1.0,
"darken_uneven_background_chunk" : true,
"debug" : false,
+ "disable_game_rendering" : false,
"draw_text_outlines" : true,
"first_time_playing" : false,
"flash_enabled" : true,
@@ -45,7 +46,7 @@ "no_pulse" : true,
"no_rotation" : false,
"no_sound" : false,
- "official" : false,
+ "official" : true,
"pixel_multiplier" : 1,
"play_swap_ready_sound" : true,
"player_focus_speed" : 4.6250,
@@ -262,9 +263,10 @@ "text_padding" : 8.0,
"text_scaling" : 1.0,
"timescale" : 1.0,
+ "use_lua_file_cache" : false,
"vsync" : false,
"windowed_auto_resolution" : false,
- "windowed_height" : 763,
- "windowed_width" : 1152,
- "zoom_factor" : 1.006553053855896
+ "windowed_height" : 901,
+ "windowed_width" : 1736,
+ "zoom_factor" : 0.8523862361907959
}
diff --git a/_RELEASE/libsdkencryptedappticket.so b/_RELEASE/libsdkencryptedappticket.so Binary files differindex 6250125f..fe77ac24 100644 --- a/_RELEASE/libsdkencryptedappticket.so +++ b/_RELEASE/libsdkencryptedappticket.so diff --git a/_RELEASE/libsteam_api.so b/_RELEASE/libsteam_api.so Binary files differindex b343945d..bbd3ff3f 100644 --- a/_RELEASE/libsteam_api.so +++ b/_RELEASE/libsteam_api.so diff --git a/_RELEASE/sdkencryptedappticket64.dll b/_RELEASE/sdkencryptedappticket64.dll Binary files differindex eef729c0..1fd4d50f 100644 --- a/_RELEASE/sdkencryptedappticket64.dll +++ b/_RELEASE/sdkencryptedappticket64.dll diff --git a/_RELEASE/steam_api64.dll b/_RELEASE/steam_api64.dll Binary files differindex 2154994c..45a49e96 100644 --- a/_RELEASE/steam_api64.dll +++ b/_RELEASE/steam_api64.dll diff --git a/_RELEASE/steam_api64.lib b/_RELEASE/steam_api64.lib Binary files differindex 2eada41f..663b11e5 100644 --- a/_RELEASE/steam_api64.lib +++ b/_RELEASE/steam_api64.lib @@ -101,6 +101,7 @@ case $OSTYPE in rm -f "$RELEASE_DIR/sfml-system-3.dll" rm -f "$RELEASE_DIR/sfml-window-3.dll" rm -f "$RELEASE_DIR/openal32.dll" + rm -f "$RELEASE_DIR/libzlib.dll" rm -f "$RELEASE_DIR/libzlib1.dll" rm -f "$RELEASE_DIR/OHWorkshopUploader.exe" rm -f "$RELEASE_DIR/SSVOpenHexagon.exe" @@ -122,13 +123,8 @@ case $OSTYPE in make $THREAD_MAKE_COUNT # Moves artifacts to release folder - mv ./_deps/sfml-build/lib/sfml-audio-3.dll $RELEASE_DIR - mv ./_deps/sfml-build/lib/sfml-graphics-3.dll $RELEASE_DIR - mv ./_deps/sfml-build/lib/sfml-network-3.dll $RELEASE_DIR - mv ./_deps/sfml-build/lib/sfml-system-3.dll $RELEASE_DIR - mv ./_deps/sfml-build/lib/sfml-window-3.dll $RELEASE_DIR cp ./_deps/sfml-src/extlibs/bin/x64/openal32.dll $RELEASE_DIR - mv ./_deps/zlib-build/libzlib1.dll $RELEASE_DIR + mv ./_deps/zlib-build/libzlib.dll $RELEASE_DIR mv ./OHWorkshopUploader.exe $RELEASE_DIR mv ./SSVOpenHexagon.exe $RELEASE_DIR ;; diff --git a/build/copylibs.sh b/build/copylibs.sh index 63ecbd5c..882a1d18 100644 --- a/build/copylibs.sh +++ b/build/copylibs.sh @@ -2,23 +2,12 @@ function copyTo { - cp ./_deps/imgui-sfml-build/libImGui-SFML_d.dll $1 & - - cp ./_deps/sfml-build/bin/sfml-graphics-d-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-system-d-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-window-d-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-network-d-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-audio-d-3.dll $1 & - - cp ./_deps/sfml-src/extlibs/bin/x64/openal32.dll $1 & - cp ./_deps/libsodium-cmake-build/libsodium.dll $1 & - cp ./_deps/luajit-build/src/libluajit.dll $1 & - cp ./_deps/zlib-build/libzlib1.dll $1 & - + cp ./_deps/zlib-build/libzlib.dll $1 & cp /c/msys64/mingw64/bin/libssp-0.dll $1 & cp /c/msys64/mingw64/bin/libstdc++-6.dll $1 & cp /c/msys64/mingw64/bin/libgcc_s_seh-1.dll $1 & cp /c/msys64/mingw64/bin/libwinpthread-1.dll $1 & + cp /c/msys64/mingw64/bin/libopenal-1.dll $1 & } copyTo "../_RELEASE" diff --git a/build/make_debug_client_win10_msys_2_copy.sh b/build/make_debug_client_win10_msys_2_copy.sh index c29110a7..ff2da458 100644 --- a/build/make_debug_client_win10_msys_2_copy.sh +++ b/build/make_debug_client_win10_msys_2_copy.sh @@ -10,7 +10,7 @@ echo "--------------------------------------------------------------------" echo "" cp ./SSVOpenHexagon.exe ../_RELEASE -cp ./SSVOpenHexagon-Console.exe ../_RELEASE +cp ./SSVOpenHexagon.exe ../_RELEASE/SSVOpenHexagon-Console.exe cp ./OHWorkshopUploader.exe ../_RELEASE cp ./OHServerControl.exe ../_RELEASE diff --git a/buildlx/cpsyslibs.sh b/buildlx/cpsyslibs.sh new file mode 100644 index 00000000..b6d770bc --- /dev/null +++ b/buildlx/cpsyslibs.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +rm -Rf ./syslibs +mkdir -p ./syslibs +cp /usr/lib/libopenal.so.1 ./syslibs +cp /usr/lib/libvorbisenc.so.2 ./syslibs +cp /usr/lib/libvorbisfile.so.3 ./syslibs +cp /usr/lib/libvorbis.so.0 ./syslibs +cp /usr/lib/libogg.so.0 ./syslibs +cp /usr/lib/libFLAC.so.12 ./syslibs +cp /usr/lib/libdl.so.2 ./syslibs +cp /usr/lib/libXcursor.so.1 ./syslibs +cp /usr/lib/libXrandr.so.2 ./syslibs +cp /usr/lib/libXext.so.6 ./syslibs +cp /usr/lib/libXrender.so.1 ./syslibs +cp /usr/lib/libXfixes.so.3 ./syslibs +cp /usr/lib/libX11.so.6 ./syslibs +cp /usr/lib/libudev.so.1 ./syslibs +cp /usr/lib/libfreetype.so.6 ./syslibs +cp /usr/lib/libGL.so.1 ./syslibs +cp /usr/lib/libGLU.so.1 ./syslibs +cp /usr/lib/libstdc++.so.6 ./syslibs +cp /usr/lib/libm.so.6 ./syslibs +cp /usr/lib/libgcc_s.so.1 ./syslibs +cp /usr/lib/libc.so.6 ./syslibs +cp /usr/lib64/ld-linux-x86-64.so.2 ./syslibs +cp /usr/lib/libpthread.so.0 ./syslibs +cp /usr/lib/librt.so.1 ./syslibs +cp /usr/lib/libxcb.so.1 ./syslibs +cp /usr/lib/libcap.so.2 ./syslibs +cp /usr/lib/libbz2.so.1.0 ./syslibs +cp /usr/lib/libpng16.so.16 ./syslibs +cp /usr/lib/libharfbuzz.so.0 ./syslibs +cp /usr/lib/libbrotlidec.so.1 ./syslibs +cp /usr/lib/libGLdispatch.so.0 ./syslibs +cp /usr/lib/libGLX.so.0 ./syslibs +cp /usr/lib/libOpenGL.so.0 ./syslibs +cp /usr/lib/libXau.so.6 ./syslibs +cp /usr/lib/libXdmcp.so.6 ./syslibs +cp /usr/lib/libglib-2.0.so.0 ./syslibs +cp /usr/lib/libgraphite2.so.3 ./syslibs +cp /usr/lib/libbrotlicommon.so.1 ./syslibs +cp /usr/lib/libpcre2-8.so.0 ./syslibs diff --git a/buildlx/make_debug_client_linux_clang_0_cmake.sh b/buildlx/make_debug_client_linux_clang_0_cmake.sh index ec00e35f..3ba44c1b 100755 --- a/buildlx/make_debug_client_linux_clang_0_cmake.sh +++ b/buildlx/make_debug_client_linux_clang_0_cmake.sh @@ -17,5 +17,5 @@ cmake .. -G"Ninja" \ -Wall -Wextra -Wpedantic -Wno-braced-scalar-init -Wno-missing-field-initializers \ -D_GLIBCXX_ASSERTIONS=1 \ -fstack-protector -Wno-pragmas \ - -fsanitize=address" + -fsanitize=address -g3" diff --git a/buildlx/make_debug_client_linux_clang_2_copy.sh b/buildlx/make_debug_client_linux_clang_2_copy.sh index 2b2a0f30..fb92ac58 100755 --- a/buildlx/make_debug_client_linux_clang_2_copy.sh +++ b/buildlx/make_debug_client_linux_clang_2_copy.sh @@ -29,56 +29,4 @@ echo "| COPYING DEPS TO VBOX DRIVE |" echo "--------------------------------------------------------------------" echo "" -cp ./_deps/sfml-build/lib/libsfml-audio-d.so.3.0 ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-network-d.so.3.0 ../_RELEASE -cp ./_deps/luajit-build/src/libluajit.so ../_RELEASE cp ./_deps/zlib-build/libz.so.1 ../_RELEASE -cp ./_deps/libsodium-cmake-build/libsodium.so ../_RELEASE -cp ./_deps/imgui-sfml-build/libImGui-SFML.so ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-graphics-d.so.3.0 ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-window-d.so.3.0 ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-system-d.so.3.0 ../_RELEASE - -echo "" -echo "" -echo "--------------------------------------------------------------------" -echo "| COPYING SYSTEM DEPS TO VBOX DRIVE |" -echo "--------------------------------------------------------------------" -echo "" - -cp /usr/lib/libdl.so.2 ../_RELEASE -cp /usr/lib/libXcursor.so.1 ../_RELEASE -cp /usr/lib/libGL.so.1 ../_RELEASE -cp /usr/lib/libpthread.so.0 ../_RELEASE -cp /usr/lib/libstdc++.so.6 ../_RELEASE -cp /usr/lib/libm.so.6 ../_RELEASE -cp /usr/lib/libgcc_s.so.1 ../_RELEASE -cp /usr/lib/libc.so.6 ../_RELEASE -cp /usr/lib/libopenal.so.1 ../_RELEASE -cp /usr/lib/libvorbisenc.so.2 ../_RELEASE -cp /usr/lib/libvorbisfile.so.3 ../_RELEASE -cp /usr/lib/libvorbis.so.0 ../_RELEASE -cp /usr/lib/libogg.so.0 ../_RELEASE -cp /usr/lib/libFLAC.so.8 ../_RELEASE -cp /usr/lib64/ld-linux-x86-64.so.2 ../_RELEASE -cp /usr/lib/librt.so.1 ../_RELEASE -cp /usr/lib/libXrender.so.1 ../_RELEASE -cp /usr/lib/libXfixes.so.3 ../_RELEASE -cp /usr/lib/libX11.so.6 ../_RELEASE -cp /usr/lib/libfreetype.so.6 ../_RELEASE -cp /usr/lib/libXrandr.so.2 ../_RELEASE -cp /usr/lib/libudev.so.1 ../_RELEASE -cp /usr/lib/libGLdispatch.so.0 ../_RELEASE -cp /usr/lib/libGLX.so.0 ../_RELEASE -cp /usr/lib/libxcb.so.1 ../_RELEASE -cp /usr/lib/libbz2.so.1.0 ../_RELEASE -cp /usr/lib/libpng16.so.16 ../_RELEASE -cp /usr/lib/libharfbuzz.so.0 ../_RELEASE -cp /usr/lib/libbrotlidec.so.1 ../_RELEASE -cp /usr/lib/libXext.so.6 ../_RELEASE -cp /usr/lib/libXau.so.6 ../_RELEASE -cp /usr/lib/libXdmcp.so.6 ../_RELEASE -cp /usr/lib/libgraphite2.so.3 ../_RELEASE -cp /usr/lib/libglib-2.0.so.0 ../_RELEASE -cp /usr/lib/libbrotlicommon.so.1 ../_RELEASE -cp /usr/lib/libpcre.so.1 ../_RELEASE diff --git a/buildlx/make_debug_client_linux_gcc_0_cmake.sh b/buildlx/make_debug_client_linux_gcc_0_cmake.sh new file mode 100755 index 00000000..d58dfe72 --- /dev/null +++ b/buildlx/make_debug_client_linux_gcc_0_cmake.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e + +echo "" +echo "" +echo "--------------------------------------------------------------------" +echo "| RUNNING CMAKE IN DEBUG MODE (NINJA) |" +echo "--------------------------------------------------------------------" +cmake .. -G"Ninja" \ + -DFORCE_COLORED_OUTPUT=1 \ + -DCMAKE_BUILD_TYPE=DEBUG \ + -DCMAKE_C_COMPILER="gcc" \ + -DCMAKE_CXX_COMPILER="g++" \ + -DCMAKE_CXX_FLAGS="\ + -O0 -fno-omit-frame-pointer \ + -Wall -Wextra -Wpedantic -Wno-braced-scalar-init -Wno-missing-field-initializers \ + -D_GLIBCXX_ASSERTIONS=1 \ + -fstack-protector -Wno-pragmas \ + -fsanitize=address -g3" + diff --git a/buildlx/make_release_client_vbox_2_copy.sh b/buildlx/make_release_client_vbox_2_copy.sh index 576fd446..52a5de02 100755 --- a/buildlx/make_release_client_vbox_2_copy.sh +++ b/buildlx/make_release_client_vbox_2_copy.sh @@ -29,56 +29,7 @@ echo "| COPYING DEPS TO VBOX DRIVE |" echo "--------------------------------------------------------------------" echo "" -cp ./_deps/sfml-build/lib/libsfml-audio.so.3.0 ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-network.so.3.0 ../_RELEASE -cp ./_deps/luajit-build/src/libluajit.so ../_RELEASE cp ./_deps/zlib-build/libz.so.1 ../_RELEASE -cp ./_deps/libsodium-cmake-build/libsodium.so ../_RELEASE -cp ./_deps/imgui-sfml-build/libImGui-SFML.so ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-graphics.so.3.0 ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-window.so.3.0 ../_RELEASE -cp ./_deps/sfml-build/lib/libsfml-system.so.3.0 ../_RELEASE - -echo "" -echo "" -echo "--------------------------------------------------------------------" -echo "| COPYING SYSTEM DEPS TO VBOX DRIVE |" -echo "--------------------------------------------------------------------" -echo "" - -cp /usr/lib/libdl.so.2 ../_RELEASE -cp /usr/lib/libXcursor.so.1 ../_RELEASE -cp /usr/lib/libGL.so.1 ../_RELEASE -cp /usr/lib/libpthread.so.0 ../_RELEASE -cp /usr/lib/libstdc++.so.6 ../_RELEASE -cp /usr/lib/libm.so.6 ../_RELEASE -cp /usr/lib/libgcc_s.so.1 ../_RELEASE -cp /usr/lib/libc.so.6 ../_RELEASE -cp /usr/lib/libopenal.so.1 ../_RELEASE -cp /usr/lib/libvorbisenc.so.2 ../_RELEASE -cp /usr/lib/libvorbisfile.so.3 ../_RELEASE -cp /usr/lib/libvorbis.so.0 ../_RELEASE -cp /usr/lib/libogg.so.0 ../_RELEASE -cp /usr/lib/libFLAC.so.8 ../_RELEASE -cp /usr/lib64/ld-linux-x86-64.so.2 ../_RELEASE -cp /usr/lib/librt.so.1 ../_RELEASE -cp /usr/lib/libXrender.so.1 ../_RELEASE -cp /usr/lib/libXfixes.so.3 ../_RELEASE -cp /usr/lib/libX11.so.6 ../_RELEASE -cp /usr/lib/libfreetype.so.6 ../_RELEASE -cp /usr/lib/libXrandr.so.2 ../_RELEASE -cp /usr/lib/libudev.so.1 ../_RELEASE -cp /usr/lib/libGLdispatch.so.0 ../_RELEASE -cp /usr/lib/libGLX.so.0 ../_RELEASE -cp /usr/lib/libxcb.so.1 ../_RELEASE -cp /usr/lib/libbz2.so.1.0 ../_RELEASE -cp /usr/lib/libpng16.so.16 ../_RELEASE -cp /usr/lib/libharfbuzz.so.0 ../_RELEASE -cp /usr/lib/libbrotlidec.so.1 ../_RELEASE -cp /usr/lib/libXext.so.6 ../_RELEASE -cp /usr/lib/libXau.so.6 ../_RELEASE -cp /usr/lib/libXdmcp.so.6 ../_RELEASE -cp /usr/lib/libgraphite2.so.3 ../_RELEASE -cp /usr/lib/libglib-2.0.so.0 ../_RELEASE -cp /usr/lib/libbrotlicommon.so.1 ../_RELEASE -cp /usr/lib/libpcre.so.1 ../_RELEASE +cp /lib/x86_64-linux-gnu/libopenal.so.1 ../_RELEASE +cp /lib/x86_64-linux-gnu/libFLAC.so.8 ../_RELEASE +cp /lib/x86_64-linux-gnu/libsndio.so.7 ./_RELEASE diff --git a/buildrel/copylibs.sh b/buildrel/copylibs.sh index ba6138f5..882a1d18 100644 --- a/buildrel/copylibs.sh +++ b/buildrel/copylibs.sh @@ -2,23 +2,12 @@ function copyTo { - cp ./_deps/imgui-sfml-build/libImGui-SFML.dll $1 & - - cp ./_deps/sfml-build/bin/sfml-graphics-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-system-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-window-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-network-3.dll $1 & - cp ./_deps/sfml-build/bin/sfml-audio-3.dll $1 & - - cp ./_deps/sfml-src/extlibs/bin/x64/openal32.dll $1 & - cp ./_deps/libsodium-cmake-build/libsodium.dll $1 & - cp ./_deps/luajit-build/src/libluajit.dll $1 & - cp ./_deps/zlib-build/libzlib1.dll $1 & - + cp ./_deps/zlib-build/libzlib.dll $1 & cp /c/msys64/mingw64/bin/libssp-0.dll $1 & cp /c/msys64/mingw64/bin/libstdc++-6.dll $1 & cp /c/msys64/mingw64/bin/libgcc_s_seh-1.dll $1 & cp /c/msys64/mingw64/bin/libwinpthread-1.dll $1 & + cp /c/msys64/mingw64/bin/libopenal-1.dll $1 & } copyTo "../_RELEASE" diff --git a/buildrel/make_release_client_win10_msys_0_cmake.sh b/buildrel/make_release_client_win10_msys_0_cmake.sh index 6602844a..1dc00baf 100644 --- a/buildrel/make_release_client_win10_msys_0_cmake.sh +++ b/buildrel/make_release_client_win10_msys_0_cmake.sh @@ -18,7 +18,7 @@ cmake .. -G"Ninja" \ -DCMAKE_CXX_FLAGS="\ -fuse-ld=lld \ -Wall -Wextra -Wpedantic -Wno-braced-scalar-init \ - -Wno-pragmas -Wno-missing-field-initializers \ + -Wno-pragmas -Wno-missing-field-initializers -Wno-array-bounds -Wno-restrict \ -Wno-stringop-overflow \ -O3 -DNDEBUG \ -frounding-math -fsignaling-nans -ffloat-store -ffp-contract=off \ diff --git a/buildrel/make_timetrace_release_client_win10_msys_0_cmake.sh b/buildrel/make_timetrace_release_client_win10_msys_0_cmake.sh index aa1181f4..5897a10e 100644 --- a/buildrel/make_timetrace_release_client_win10_msys_0_cmake.sh +++ b/buildrel/make_timetrace_release_client_win10_msys_0_cmake.sh @@ -18,7 +18,7 @@ cmake .. -G"Ninja" \ -DCMAKE_CXX_FLAGS="\ -fuse-ld=lld \ -Wall -Wextra -Wpedantic -Wno-braced-scalar-init \ - -Wno-pragmas -Wno-missing-field-initializers \ + -Wno-pragmas -Wno-missing-field-initializers -Wno-braced-scalar-init \ -O3 -DNDEBUG \ -frounding-math -ffp-contract=off \ -ftime-trace \ diff --git a/include/SSVOpenHexagon/Components/CCustomWall.hpp b/include/SSVOpenHexagon/Components/CCustomWall.hpp index 58a5cd4e..a8c1ae0d 100644 --- a/include/SSVOpenHexagon/Components/CCustomWall.hpp +++ b/include/SSVOpenHexagon/Components/CCustomWall.hpp @@ -61,7 +61,10 @@ public: [[nodiscard, gnu::always_inline]] bool isOverlapping( const sf::Vector2f& point) const noexcept { - return Utils::pointInPolygon(_vertexPositions, point.x, point.y); + // Cannot use `pointInFourVertexPolygon` here due to vertex ordering + // requirements. + + return Utils::pointInPolygon<4>(_vertexPositions, point.x, point.y); } [[gnu::always_inline]] void setVertexPos( diff --git a/include/SSVOpenHexagon/Components/CPlayer.hpp b/include/SSVOpenHexagon/Components/CPlayer.hpp index 61c14c9e..3caab52f 100644 --- a/include/SSVOpenHexagon/Components/CPlayer.hpp +++ b/include/SSVOpenHexagon/Components/CPlayer.hpp @@ -21,15 +21,15 @@ class CCustomWall; class CPlayer { private: - sf::Vector2f _startPos; // Position at start of the level. + sf::Vector2f _startPos; // Position at start of the level. - sf::Vector2f _pos; // Actual position of player. + sf::Vector2f _pos; // Actual position of player. sf::Vector2f _prePushPos; // Position before the player is pushed by a wall. // Unlike `pos` it is not updated after a // successful wall push. - sf::Vector2f _lastPos; // Position of the player in the previous frame, + sf::Vector2f _lastPos; // Position of the player in the previous frame, // adjusted according to the current frame's radius. float _hue; @@ -42,12 +42,12 @@ private: bool _dead; bool _justSwapped; - bool _forcedMove; // Wherever player has been forcefully moved - // with a setPlayerAngle() call. Essential - // for proper behavior of collision calculation, - // especially on levels that make heavy usage of it. + bool _forcedMove; // Wherever player has been forcefully moved + // with a setPlayerAngle() call. Essential + // for proper behavior of collision calculation, + // especially on levels that make heavy usage of it. - float _radius; // Cached value of the radius in the current frame. + float _radius; // Cached value of the radius in the current frame. float _maxSafeDistance; // The maximum distance that there can be between // the current player position and the closest @@ -55,7 +55,7 @@ private: // overlaps with. If the closest position is further // away player cannot be saved. - float _currentSpeed; // Cached player speed in the current frame. + float _currentSpeed; // Cached player speed in the current frame. float _triangleWidth; // Visual width of the triangle, varies when focusing. float _triangleWidthTransitionTime; // From 0 to 1, when transitioning diff --git a/include/SSVOpenHexagon/Components/CWall.hpp b/include/SSVOpenHexagon/Components/CWall.hpp index 34b410a7..21ad8c27 100644 --- a/include/SSVOpenHexagon/Components/CWall.hpp +++ b/include/SSVOpenHexagon/Components/CWall.hpp @@ -8,7 +8,6 @@ #include "SSVOpenHexagon/Utils/PointInPolygon.hpp" #include "SSVOpenHexagon/Utils/FastVertexVector.hpp" -#include <SSVStart/Utils/Vector2.hpp> #include <SFML/System/Vector2.hpp> #include <array> @@ -42,10 +41,30 @@ public: void update(const float wallSpawnDist, const float radius, const sf::Vector2f& centerPos, const ssvu::FT ft); + [[gnu::always_inline]] void moveVertexAlongCurveImpl(sf::Vector2f& vertex, + const sf::Vector2f& centerPos, const float xSin, + const float xCos) const noexcept + { + const float tempX = vertex.x - centerPos.x; + const float tempY = vertex.y - centerPos.y; + vertex.x = tempX * xCos - tempY * xSin + centerPos.x; + vertex.y = tempX * xSin + tempY * xCos + centerPos.y; + } + + [[gnu::always_inline]] float getCurveRadians( + const ssvu::FT ft) const noexcept + { + constexpr float divBy60 = 1.f / 60.f; + return _curve._speed * divBy60 * ft; + } + [[gnu::always_inline]] void moveVertexAlongCurve(sf::Vector2f& vertex, - const sf::Vector2f& centerPos, const ssvu::FT ft) const + const sf::Vector2f& centerPos, const ssvu::FT ft) const noexcept { - ssvs::rotateRadAround(vertex, centerPos, _curve._speed / 60.f * ft); + const float rad = getCurveRadians(ft); + + moveVertexAlongCurveImpl( + vertex, centerPos, std::sin(rad), std::cos(rad)); } void draw(sf::Color color, Utils::FastVertexVectorTris& wallQuads); @@ -71,7 +90,9 @@ public: [[nodiscard, gnu::always_inline]] bool isOverlapping( const sf::Vector2f& point) const noexcept { - return Utils::pointInPolygon(_vertexPositions, point.x, point.y); + return Utils::pointInFourVertexPolygon(_vertexPositions[0], + _vertexPositions[1], _vertexPositions[2], _vertexPositions[3], + point); } [[nodiscard, gnu::always_inline]] constexpr bool diff --git a/include/SSVOpenHexagon/Core/HexagonDialogBox.hpp b/include/SSVOpenHexagon/Core/HexagonDialogBox.hpp index ff43cfbe..f9680ef3 100644 --- a/include/SSVOpenHexagon/Core/HexagonDialogBox.hpp +++ b/include/SSVOpenHexagon/Core/HexagonDialogBox.hpp @@ -5,13 +5,16 @@ #pragma once #include <SFML/Graphics/Color.hpp> -#include <SFML/Graphics/Font.hpp> #include <SFML/Graphics/Text.hpp> #include <SFML/Window/Keyboard.hpp> #include <string> #include <vector> +namespace sf { +class Font; +} + namespace ssvs { class GameWindow; } diff --git a/include/SSVOpenHexagon/Core/HexagonGame.hpp b/include/SSVOpenHexagon/Core/HexagonGame.hpp index 237624f0..e1e6d065 100644 --- a/include/SSVOpenHexagon/Core/HexagonGame.hpp +++ b/include/SSVOpenHexagon/Core/HexagonGame.hpp @@ -48,6 +48,7 @@ #include <optional> #include <vector> #include <string> +#include <string_view> struct ImGuiInputTextCallbackData; @@ -305,10 +306,10 @@ public: void initLuaAndPrintDocs(); - void luaExceptionLippincottHandler(const std::string& mName); + void luaExceptionLippincottHandler(std::string_view mName); template <typename T, typename... TArgs> - auto runLuaFunctionIfExists(const std::string& mName, const TArgs&... mArgs) + auto runLuaFunctionIfExists(std::string_view mName, const TArgs&... mArgs) try { return Utils::runLuaFunctionIfExists<T, TArgs...>(lua, mName, mArgs...); diff --git a/include/SSVOpenHexagon/Data/CapColor.hpp b/include/SSVOpenHexagon/Data/CapColor.hpp index 1c6df3e5..7e999da0 100644 --- a/include/SSVOpenHexagon/Data/CapColor.hpp +++ b/include/SSVOpenHexagon/Data/CapColor.hpp @@ -6,7 +6,7 @@ #include "SSVOpenHexagon/Data/ColorData.hpp" -#include <variant> +#include "SSVOpenHexagon/Utils/TinyVariant.hpp" namespace Json { class Value; @@ -28,11 +28,11 @@ struct ByIndex { int _index; }; } // namespace CapColorMode -using CapColor = std::variant< // - CapColorMode::Main, // - CapColorMode::MainDarkened, // - CapColorMode::ByIndex, // - ColorData // +using CapColor = vittorioromeo::tinyvariant< // + CapColorMode::Main, // + CapColorMode::MainDarkened, // + CapColorMode::ByIndex, // + ColorData // >; [[nodiscard]] CapColor parseCapColor(const ssvuj::Obj& obj) noexcept; diff --git a/include/SSVOpenHexagon/Global/Assert.hpp b/include/SSVOpenHexagon/Global/Assert.hpp index b34c9dc6..73c7b98a 100644 --- a/include/SSVOpenHexagon/Global/Assert.hpp +++ b/include/SSVOpenHexagon/Global/Assert.hpp @@ -4,8 +4,6 @@ #pragma once -#include <SSVUtils/Core/Common/LikelyUnlikely.hpp> - #include <vrm/pp/sep_to_str.hpp> namespace hg::Impl { @@ -23,7 +21,7 @@ namespace hg::Impl { constexpr const char* assert_code = \ VRM_PP_SEP_TOSTR(" ", VRM_PP_EMPTY(), __VA_ARGS__); \ \ - if(SSVU_UNLIKELY(!static_cast<bool>(__VA_ARGS__))) \ + if(!static_cast<bool>(__VA_ARGS__)) [[unlikely]] \ { \ ::hg::Impl::assertionFailure(assert_code, __FILE__, __LINE__); \ } \ diff --git a/include/SSVOpenHexagon/Global/Assets.hpp b/include/SSVOpenHexagon/Global/Assets.hpp index 82068e32..601170e0 100644 --- a/include/SSVOpenHexagon/Global/Assets.hpp +++ b/include/SSVOpenHexagon/Global/Assets.hpp @@ -4,19 +4,9 @@ #pragma once -#include "SSVOpenHexagon/Data/LevelData.hpp" -#include "SSVOpenHexagon/Data/PackData.hpp" -#include "SSVOpenHexagon/Data/ProfileData.hpp" -#include "SSVOpenHexagon/Data/StyleData.hpp" -#include "SSVOpenHexagon/Data/LoadInfo.hpp" -#include "SSVOpenHexagon/Data/PackInfo.hpp" - #include "SSVOpenHexagon/Utils/UniquePtr.hpp" -#include <SFML/Graphics/Shader.hpp> - #include <cstddef> -#include <map> #include <optional> #include <string> #include <unordered_map> @@ -41,80 +31,20 @@ namespace Steam { class steam_manager; } +class ProfileData; +struct LoadInfo; class MusicData; class AssetStorage; +struct LevelData; +struct PackData; +struct PackInfo; +class StyleData; class HGAssets { private: - Steam::steam_manager* steamManager; - - bool levelsOnly{false}; - - Utils::UniquePtr<AssetStorage> assetStorage; - - std::unordered_map<std::string, LevelData> levelDatas; - std::unordered_map<std::string, std::vector<std::string>> - levelDataIdsByPack; - - std::unordered_map<std::string, PackData> packDatas; - - std::vector<PackInfo> packInfos; - std::vector<PackInfo> selectablePackInfos; - - std::unordered_map<std::string, std::string> musicPathMap; - std::map<std::string, MusicData> musicDataMap; - std::map<std::string, StyleData> styleDataMap; - std::map<std::string, ProfileData> profileDataMap; - ProfileData* currentProfilePtr{nullptr}; - - std::unordered_set<std::string> packIdsWithMissingDependencies; - - struct LoadedShader - { - Utils::UniquePtr<sf::Shader> shader; - std::string path; - sf::Shader::Type shaderType; - std::size_t id; - }; - - std::unordered_map<std::string, LoadedShader> shaders; - std::unordered_map<std::string, std::size_t> shadersPathToId; - std::vector<sf::Shader*> shadersById; - - std::string buf; - - template <typename... Ts> - [[nodiscard]] std::string& concatIntoBuf(const Ts&...); - - [[nodiscard]] bool loadAllPackDatas(); - [[nodiscard]] bool loadAllPackAssets(const bool headless); - [[nodiscard]] bool loadWorkshopPackDatasFromCache(); - [[nodiscard]] bool verifyAllPackDependencies(); - [[nodiscard]] bool loadAllLocalProfiles(); - - [[nodiscard]] bool loadPackData(const ssvufs::Path& packPath); - - [[nodiscard]] bool loadPackAssets( - const PackData& packData, const bool headless); - - void loadPackAssets_loadShaders(const std::string& mPackId, - const ssvufs::Path& mPath, const bool headless); - void loadPackAssets_loadMusic( - const std::string& mPackId, const ssvufs::Path& mPath); - void loadPackAssets_loadMusicData( - const std::string& mPackId, const ssvufs::Path& mPath); - void loadPackAssets_loadStyleData( - const std::string& mPackId, const ssvufs::Path& mPath); - void loadPackAssets_loadLevelData( - const std::string& mPackId, const ssvufs::Path& mPath); - void loadPackAssets_loadCustomSounds( - const std::string& mPackId, const ssvufs::Path& mPath); - - [[nodiscard]] std::string getCurrentLocalProfileFilePath(); - -private: - LoadInfo loadInfo; + class HGAssetsImpl; + Utils::UniquePtr<HGAssetsImpl> _impl; public: HGAssets(Steam::steam_manager* mSteamManager, bool mHeadless, @@ -122,10 +52,6 @@ public: ~HGAssets(); - // When the Steam API can not be retrieved, this set holds pack ids - // retrieved from the cache to try and load the workshop packs installed - std::unordered_set<std::string> cachedWorkshopPackIds; - [[nodiscard]] LoadInfo& getLoadResults(); [[nodiscard]] sf::Texture& getTexture(const std::string& mId); @@ -215,6 +141,12 @@ public: getPackIdsWithMissingDependencies() const noexcept; void addLocalProfile(ProfileData&& profileData); + + [[nodiscard]] std::unordered_map<std::string, std::string>& + getLuaFileCache(); + + [[nodiscard]] const std::unordered_map<std::string, std::string>& + getLuaFileCache() const; }; } // namespace hg diff --git a/include/SSVOpenHexagon/Global/Config.hpp b/include/SSVOpenHexagon/Global/Config.hpp index 61e80728..1024da5f 100644 --- a/include/SSVOpenHexagon/Global/Config.hpp +++ b/include/SSVOpenHexagon/Global/Config.hpp @@ -93,6 +93,8 @@ void setPlayerTrailHasSwapColor(bool x); void setShowSwapParticles(bool x); void setPlaySwapReadySound(bool x); void setShowSwapBlinkingEffect(bool x); +void setUseLuaFileCache(bool x); +void setDisableGameRendering(bool x); [[nodiscard]] bool getOfficial(); [[nodiscard]] const std::string& getUneligibilityReason(); @@ -156,7 +158,7 @@ void setShowSwapBlinkingEffect(bool x); [[nodiscard]] const std::string& getServerIp(); [[nodiscard]] unsigned short getServerPort(); [[nodiscard]] unsigned short getServerControlPort(); -[[nodiscard]] const std::vector<std::string> getServerLevelWhitelist(); +[[nodiscard]] const std::vector<std::string>& getServerLevelWhitelist(); [[nodiscard]] bool getSaveLastLoginUsername(); [[nodiscard]] const std::string& getLastLoginUsername(); [[nodiscard]] bool getShowLoginAtStartup(); @@ -170,6 +172,8 @@ void setShowSwapBlinkingEffect(bool x); [[nodiscard]] bool getShowSwapParticles(); [[nodiscard]] bool getPlaySwapReadySound(); [[nodiscard]] bool getShowSwapBlinkingEffect(); +[[nodiscard]] bool getUseLuaFileCache(); +[[nodiscard]] bool getDisableGameRendering(); // keyboard binds @@ -214,8 +218,7 @@ void clearTriggerBind(ssvs::Input::Trigger& trig, const int index); void joystickBindsSanityCheck(); -[[nodiscard]] const std::string getJoystickBindNames( - const Joystick::Jid bindID); +[[nodiscard]] std::string getJoystickBindName(const Joystick::Jid bindID); using JoystickTriggerGetter = unsigned int (*)(); extern const std::array<JoystickTriggerGetter, diff --git a/include/SSVOpenHexagon/Global/PCH.hpp b/include/SSVOpenHexagon/Global/PCH.hpp index a28f3f14..8794d8cc 100644 --- a/include/SSVOpenHexagon/Global/PCH.hpp +++ b/include/SSVOpenHexagon/Global/PCH.hpp @@ -144,7 +144,6 @@ #include <SSVUtils/Core/Common/Common.hpp> #include <SSVUtils/Core/Common/Frametime.hpp> -#include <SSVUtils/Core/Common/LikelyUnlikely.hpp> #include <SSVUtils/Core/FileSystem/FileSystem.hpp> #include <SSVUtils/Core/Log/Log.hpp> #include <SSVUtils/Core/Utils/Containers.hpp> @@ -198,6 +197,7 @@ extern template class std::vector<std::string>; extern template class std::optional<int>; extern template class std::optional<std::size_t>; +extern template class std::optional<std::string>; extern template class std::unordered_map<std::string, float>; extern template class std::unordered_map<float, std::string>; diff --git a/include/SSVOpenHexagon/Global/Version.hpp b/include/SSVOpenHexagon/Global/Version.hpp index d62cf4de..e841f919 100644 --- a/include/SSVOpenHexagon/Global/Version.hpp +++ b/include/SSVOpenHexagon/Global/Version.hpp @@ -43,7 +43,7 @@ struct GameVersion } }; -inline constexpr GameVersion GAME_VERSION{2, 1, 6}; -inline constexpr auto& GAME_VERSION_STR = "2.1.6"; +inline constexpr GameVersion GAME_VERSION{2, 1, 7}; +inline constexpr auto& GAME_VERSION_STR = "2.1.7"; } // namespace hg diff --git a/include/SSVOpenHexagon/Online/Database.hpp b/include/SSVOpenHexagon/Online/Database.hpp index d9638c23..b28a10ba 100644 --- a/include/SSVOpenHexagon/Online/Database.hpp +++ b/include/SSVOpenHexagon/Online/Database.hpp @@ -10,7 +10,6 @@ #include <cstdint> #include <optional> #include <vector> -#include <chrono> // TODO (P2): remove reliance on steam ID for future platforms diff --git a/include/SSVOpenHexagon/SSVUtilsJson/JsonCpp/jsoncpp.inl b/include/SSVOpenHexagon/SSVUtilsJson/JsonCpp/jsoncpp.inl index 43fb4d95..461959e7 100644 --- a/include/SSVOpenHexagon/SSVUtilsJson/JsonCpp/jsoncpp.inl +++ b/include/SSVOpenHexagon/SSVUtilsJson/JsonCpp/jsoncpp.inl @@ -1780,9 +1780,11 @@ inline void Path::addPathInArg(const std::string&, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) { if(itInArg == in.end()) - {} + { + } else if((*itInArg)->kind_ != kind) - {} + { + } else args_.emplace_back(**itInArg); } diff --git a/include/SSVOpenHexagon/Utils/Concat.hpp b/include/SSVOpenHexagon/Utils/Concat.hpp index a77f317b..b4443671 100644 --- a/include/SSVOpenHexagon/Utils/Concat.hpp +++ b/include/SSVOpenHexagon/Utils/Concat.hpp @@ -58,7 +58,8 @@ template <std::size_t N> { const char* end = s; while(*end++ != 0) - {} + { + } return end - s - 1; } diff --git a/include/SSVOpenHexagon/Utils/FastVertexVector.hpp b/include/SSVOpenHexagon/Utils/FastVertexVector.hpp index 96a47979..3ad9670f 100644 --- a/include/SSVOpenHexagon/Utils/FastVertexVector.hpp +++ b/include/SSVOpenHexagon/Utils/FastVertexVector.hpp @@ -15,8 +15,6 @@ #include <SFML/Graphics/RenderTarget.hpp> #include <SFML/Graphics/Vertex.hpp> -#include <SSVUtils/Core/Common/LikelyUnlikely.hpp> - #include <cstddef> #include <cstring> @@ -51,14 +49,14 @@ public: void reserve(const std::size_t n) { - if(SSVU_LIKELY(_capacity >= n)) + if(_capacity >= n) [[likely]] { return; } auto new_data = Utils::makeUniqueArray<VertexUnion>(n); - if(SSVU_UNLIKELY(_data != nullptr)) + if(_data != nullptr) [[unlikely]] { std::memcpy( new_data.get(), _data.get(), sizeof(sf::Vertex) * _size); @@ -69,7 +67,7 @@ public: SSVOH_ASSERT(_capacity == 0); } - _data = std::move(new_data); + _data = SSVOH_MOVE(new_data); _capacity = n; } @@ -78,7 +76,7 @@ public: { SSVOH_ASSERT(_size + rhs._size <= _capacity); - if(SSVU_UNLIKELY(rhs.size() == 0)) + if(rhs.size() == 0) [[unlikely]] { return; } @@ -123,7 +121,7 @@ public: void draw(sf::RenderTarget& mRenderTarget, const sf::RenderStates& mRenderStates) const override { - if(SSVU_UNLIKELY(_data == nullptr)) + if(_data == nullptr) [[unlikely]] { SSVOH_ASSERT(_size == 0); SSVOH_ASSERT(_capacity == 0); diff --git a/include/SSVOpenHexagon/Utils/LuaMetadataProxy.hpp b/include/SSVOpenHexagon/Utils/LuaMetadataProxy.hpp index b3c7d7bd..9fbdbd9f 100644 --- a/include/SSVOpenHexagon/Utils/LuaMetadataProxy.hpp +++ b/include/SSVOpenHexagon/Utils/LuaMetadataProxy.hpp @@ -70,15 +70,14 @@ private: [&]<std::size_t... Is>(std::index_sequence<Is...>) { - (( // + (( // res += typeToStr(TypeWrapper< std::decay_t<typename AE::template NthArg<Is>>>{}), // res += ' ', // res += self->argNames.at(Is), // res += ", "), ...); - } - (std::make_index_sequence<AE::numArgs>{}); + }(std::make_index_sequence<AE::numArgs>{}); res.pop_back(); res.pop_back(); diff --git a/include/SSVOpenHexagon/Utils/LuaWrapper.hpp b/include/SSVOpenHexagon/Utils/LuaWrapper.hpp index 1ee6858f..69de1f8a 100644 --- a/include/SSVOpenHexagon/Utils/LuaWrapper.hpp +++ b/include/SSVOpenHexagon/Utils/LuaWrapper.hpp @@ -45,6 +45,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sstream> #include <stdexcept> #include <string> +#include <string_view> #include <tuple> #include <type_traits> #include <vector> @@ -130,8 +131,8 @@ public: LuaContext(const LuaContext&) = delete; LuaContext& operator=(const LuaContext&) = delete; - LuaContext(LuaContext&& s); - LuaContext& operator=(LuaContext&& s); + LuaContext(LuaContext&& s) noexcept; + LuaContext& operator=(LuaContext&& s) noexcept; ~LuaContext(); @@ -185,27 +186,26 @@ public: /// \brief Executes lua code given as parameter \param code A string /// containing code that will be executed by lua - [[gnu::always_inline]] inline void executeCode(const std::string& code) + [[gnu::always_inline]] inline void executeCode(std::string_view code) { _load(code); _call<std::tuple<>>(std::tuple<>()); } - /// \brief Executes lua code given as parameter and returns a value - /// \param code A string containing code that will be executed by lua + /// \brief Executes lua code from the stream and returns a value \param + /// code A stream that lua will read its code from template <typename T> [[nodiscard, gnu::always_inline]] inline T executeCode( - const std::string& code) + std::string_view code) { - std::istringstream str(code); - return executeCode<T>(str); + _load(code); + return _call<T>(std::tuple<>()); } - /// \brief Tells that lua will be allowed to access an object's function template <typename T, typename R, typename... Args> [[gnu::always_inline]] inline void registerFunction( - const std::string& name, R (T::*f)(Args...)) + std::string_view name, R (T::*f)(Args...)) { _registerFunction(name, [f](const std::shared_ptr<T>& ptr, Args... args) { return ((*ptr).*f)(args...); }); @@ -213,7 +213,7 @@ public: template <typename T, typename R, typename... Args> [[gnu::always_inline]] inline void registerFunction( - const std::string& name, R (T::*f)(Args...) const) + std::string_view name, R (T::*f)(Args...) const) { _registerFunction(name, [f](const std::shared_ptr<T>& ptr, Args... args) { return ((*ptr).*f)(args...); }); @@ -221,7 +221,7 @@ public: template <typename T, typename R, typename... Args> [[gnu::always_inline]] inline void registerFunction( - const std::string& name, R (T::*f)(Args...) volatile) + std::string_view name, R (T::*f)(Args...) volatile) { _registerFunction(name, [f](const std::shared_ptr<T>& ptr, Args... args) { return ((*ptr).*f)(args...); }); @@ -229,7 +229,7 @@ public: template <typename T, typename R, typename... Args> [[gnu::always_inline]] inline void registerFunction( - const std::string& name, R (T::*f)(Args...) const volatile) + std::string_view name, R (T::*f)(Args...) const volatile) { _registerFunction(name, [f](const std::shared_ptr<T>& ptr, Args... args) { return ((*ptr).*f)(args...); }); @@ -241,15 +241,14 @@ public: /// \param fn Function which takes as first parameter a std::shared_ptr template <typename T> [[gnu::always_inline]] inline void registerFunction( - const std::string& name, T&& fn, decltype(&T::operator())* = nullptr) + std::string_view name, T&& fn, decltype(&T::operator())* = nullptr) { _registerFunction(name, SSVOH_FWD(fn)); } /// \brief Inverse operation of registerFunction template <typename T> - [[gnu::always_inline]] inline void unregisterFunction( - const std::string& name) + [[gnu::always_inline]] inline void unregisterFunction(std::string_view name) { _unregisterFunction<T>(name); } @@ -261,7 +260,7 @@ public: /// \param ... Parameters to pass to the function template <typename R, typename... Args> [[nodiscard, gnu::always_inline]] inline R callLuaFunction( - const std::string& mVarName, Args&&... args) + std::string_view mVarName, Args&&... args) { _getGlobal(mVarName); return _call<R>(std::make_tuple(SSVOH_FWD(args)...)); @@ -270,7 +269,7 @@ public: /// \brief Returns true if the value of the variable is an array \param /// mVarName Name of the variable to check [[nodiscard, gnu::always_inline]] inline bool isVariableArray( - const std::string& mVarName) const + std::string_view mVarName) const { _getGlobal(mVarName); @@ -284,7 +283,7 @@ public: /// writeArrayIntoVariable("myArr"); writeVariable("myArr.something", /// 5); [[gnu::always_inline]] inline void writeArrayIntoVariable( - const std::string& mVarName) + std::string_view mVarName) { lua_newtable(_state); _setGlobal(mVarName); @@ -292,7 +291,7 @@ public: /// \brief Returns true if variable exists (ie. not nil) [[nodiscard, gnu::always_inline]] inline bool doesVariableExist( - const std::string& mVarName) const + std::string_view mVarName) const { _getGlobal(mVarName); @@ -302,8 +301,7 @@ public: } /// \brief Destroys a variable \details Puts the nil value into it - [[gnu::always_inline]] inline void clearVariable( - const std::string& mVarName) + [[gnu::always_inline]] inline void clearVariable(std::string_view mVarName) { lua_pushnil(_state); _setGlobal(mVarName); @@ -315,7 +313,7 @@ public: /// value using a std::shared_ptr<T> template <typename T> [[nodiscard, gnu::always_inline]] inline T readVariable( - const std::string& mVarName) const + std::string_view mVarName) const { _getGlobal(mVarName); return _readTopAndPop(1, (T*)nullptr); @@ -324,7 +322,7 @@ public: /// \brief template <typename T> [[nodiscard, gnu::always_inline]] inline bool readVariableIfExists( - const std::string& mVarName, T& out) + std::string_view mVarName, T& out) { if(!doesVariableExist(mVarName)) { @@ -340,7 +338,7 @@ public: /// std::string, std::function or ObjectWrapper<...>. All objects are /// passed by copy and destroyed by the garbage collector. template <typename T> - void writeVariable(const std::string& mVarName, T&& data) + void writeVariable(std::string_view mVarName, T&& data) { static_assert(!std::is_same_v<std::tuple<T>, T>, "Error: you can't use LuaContext::writeVariable with a tuple"); @@ -383,8 +381,8 @@ private: // important: _setGlobal will pop the value even if it throws an // exception, while _getGlobal won't push the value if it throws an // exception - void _getGlobal(const std::string& mVarName) const; - void _setGlobal(const std::string& mVarName); + void _getGlobal(std::string_view mVarName) const; + void _setGlobal(std::string_view mVarName); // simple function that reads the top # elements of the stack, pops // them, and returns them @@ -397,7 +395,7 @@ private: { try { - const R value = _read(-nb, ptr); + R value = _read(-nb, ptr); lua_pop(_state, nb); return value; } @@ -426,7 +424,7 @@ private: // function names and values are functions // (where type is the first parameter of the functor) template <typename T> - void _registerFunction(const std::string& name, T&& function) + void _registerFunction(std::string_view name, T&& function) { using FunctionType = typename RemoveMemberPtr<decltype(&T::operator())>::Type; @@ -434,7 +432,7 @@ private: using ObjectType = typename std::tuple_element_t<0, typename FnTupleWrapper<FunctionType>::ParamsType>::element_type; - _registerFunctionImpl(name.c_str(), typeid(ObjectType)); + _registerFunctionImpl(name.data(), typeid(ObjectType)); _push(SSVOH_FWD(function)); lua_settable(_state, -3); @@ -443,7 +441,7 @@ private: // inverse operation of _registerFunction template <typename T> - void _unregisterFunction(const std::string& name) + void _unregisterFunction(std::string_view name) { // trying to get the existing functions list lua_pushlightuserdata(_state, const_cast<std::type_info*>(&typeid(T))); @@ -455,7 +453,7 @@ private: return; } - lua_pushstring(_state, name.c_str()); + lua_pushstring(_state, name.data()); lua_pushnil(_state); lua_settable(_state, -3); lua_pop(_state, 1); @@ -468,7 +466,7 @@ private: // top of the stack // it is defined in the .cpp void _load(std::istream& code); - void _load(const std::string& code); + void _load(std::string_view code); // this function calls what is on the top of the stack and removes it // (just like lua_call) @@ -544,17 +542,8 @@ public: { public: Table() = default; - - Table(Table&& t) - { - swap(t, *this); - } - - Table& operator=(Table&& t) - { - swap(t, *this); - return *this; - } + Table(Table&& t) noexcept = default; + Table& operator=(Table&& t) noexcept = default; template <typename... Args> explicit Table(Args&&... args) @@ -627,7 +616,7 @@ public: Element(Key&& k, Value&& v) : key(SSVOH_FWD(k)), value(SSVOH_FWD(v)) {} - void push(LuaContext& ctxt) const + void push(LuaContext& ctxt) const override { SSVOH_ASSERT(lua_istable(ctxt._state, -1)); @@ -684,9 +673,9 @@ private: return 1; } - [[gnu::always_inline]] inline int _push(const std::string& s) + [[gnu::always_inline]] inline int _push(std::string_view s) { - lua_pushstring(_state, s.c_str()); + lua_pushstring(_state, s.data()); return 1; } @@ -930,11 +919,9 @@ private: template <typename... Args> [[gnu::always_inline]] inline int _push(const std::tuple<Args...>& t) { - return [ this, &t ]<int... Is>(std::integer_sequence<int, Is...>) - { + return [this, &t]<int... Is>(std::integer_sequence<int, Is...>) { return (this->_push(std::get<Is>(t)) + ... + 0); - } - (std::make_integer_sequence<int, sizeof...(Args)>{}); + }(std::make_integer_sequence<int, sizeof...(Args)>{}); } /**************************************************/ @@ -1110,12 +1097,11 @@ return table;*/ [[gnu::always_inline]] inline auto _read( const int index, std::tuple<Ts...> const* = nullptr) { - return [ this, index ]<int... Is>(std::integer_sequence<int, Is...>) + return [this, index]<int... Is>(std::integer_sequence<int, Is...>) { return std::make_tuple(this->_read( index + Is, static_cast<std::decay_t<Ts>*>(nullptr))...); - } - (std::make_integer_sequence<int, sizeof...(Ts)>{}); + }(std::make_integer_sequence<int, sizeof...(Ts)>{}); } [[gnu::always_inline]] inline constexpr std::tuple<> _read( diff --git a/include/SSVOpenHexagon/Utils/PointInPolygon.hpp b/include/SSVOpenHexagon/Utils/PointInPolygon.hpp index 6d8b569e..01b7ca09 100644 --- a/include/SSVOpenHexagon/Utils/PointInPolygon.hpp +++ b/include/SSVOpenHexagon/Utils/PointInPolygon.hpp @@ -4,16 +4,17 @@ #pragma once +#include <SFML/System/Vector2.hpp> + namespace hg::Utils { -template <typename TC, typename T> +template <std::size_t N, typename TC, typename T> [[gnu::always_inline, gnu::pure, nodiscard]] inline bool pointInPolygon( - const TC& mVertices, T x, T y) noexcept + const TC& mVertices, const T x, const T y) noexcept { bool result{false}; - const auto size{mVertices.size()}; - for(decltype(mVertices.size()) i{0}, j{size - 1}; i < size; j = i++) + for(std::size_t i{0}, j{N - 1}; i < N; j = i++) { const auto& vI{mVertices[i]}; const auto& vJ{mVertices[j]}; @@ -28,4 +29,31 @@ template <typename TC, typename T> return result; } + +[[gnu::always_inline, gnu::pure, nodiscard]] inline bool +pointInFourVertexPolygon(const sf::Vector2f& a, const sf::Vector2f& b, + const sf::Vector2f& c, const sf::Vector2f& d, + const sf::Vector2f& point) noexcept +{ + const sf::Vector2f ab = b - a; + const sf::Vector2f bc = c - b; + const sf::Vector2f cd = d - c; + const sf::Vector2f da = a - d; + + const sf::Vector2f ap_ab = point - a; + const sf::Vector2f bp_bc = point - b; + const sf::Vector2f cp_cd = point - c; + const sf::Vector2f dp_da = point - d; + + const float ab_x_ap = ab.cross(ap_ab); + const float bc_x_bp = bc.cross(bp_bc); + const float cd_x_cp = cd.cross(cp_cd); + const float da_x_dp = da.cross(dp_da); + + return (ab_x_ap <= 0.f && bc_x_bp <= 0.f && cd_x_cp <= 0.f && + da_x_dp <= 0.f) || + (ab_x_ap >= 0.f && bc_x_bp >= 0.f && cd_x_cp >= 0.f && + da_x_dp >= 0.f); +} + } // namespace hg::Utils diff --git a/include/SSVOpenHexagon/Utils/TinyVariant.hpp b/include/SSVOpenHexagon/Utils/TinyVariant.hpp new file mode 100644 index 00000000..e7edd326 --- /dev/null +++ b/include/SSVOpenHexagon/Utils/TinyVariant.hpp @@ -0,0 +1,738 @@ +#pragma once + +#include <cassert> +#include <cstdint> // TODO: remove dependency? +#include <initializer_list> // TODO: remove dependency? +#include <new> // TODO: remove dependency? + +#if ((__GNUC__ >= 10) || defined(__clang__)) && !defined(_MSC_VER) +#define TINYVARIANT_SUPPORTS_HAS_BUILTIN +#endif + +#ifdef TINYVARIANT_SUPPORTS_HAS_BUILTIN +#if __has_builtin(__make_integer_seq) +#define TINYVARIANT_USE_MAKE_INTEGER_SEQ +#elif __has_builtin(__integer_pack) +#define TINYVARIANT_USE_INTEGER_PACK +#else +#define TINYVARIANT_USE_STD_INDEX_SEQUENCE +#endif +#else +#define TINYVARIANT_USE_STD_INDEX_SEQUENCE +#endif + +#ifdef TINYVARIANT_USE_STD_INDEX_SEQUENCE +#include <utility> +#endif + +namespace vittorioromeo::impl { + +template <typename T> +T&& declval(); + +template <typename...> +struct common_type_between; + +template <typename T> +struct common_type_between<T> +{ + using type = T; +}; + +template <typename T> +struct common_type_between<T, T> +{ + using type = T; +}; + +template <typename T, typename U, typename... Rest> +struct common_type_between<T, U, Rest...> +{ + using type = typename common_type_between< + decltype(true ? declval<T>() : declval<U>()), Rest...>::type; +}; + +using sz_t = decltype(sizeof(int)); + +#ifdef TINYVARIANT_USE_STD_INDEX_SEQUENCE + +template <sz_t... Is> +using index_sequence = std::index_sequence<Is...>; + +#else + +template <sz_t...> +struct index_sequence +{}; + +#endif + +#ifdef TINYVARIANT_USE_MAKE_INTEGER_SEQ + +template <typename, sz_t... X> +struct index_sequence_helper +{ + using type = index_sequence<X...>; +}; + +template <sz_t N> +using index_sequence_up_to = + typename __make_integer_seq<index_sequence_helper, sz_t, N>::type; + +#elif defined(TINYVARIANT_USE_INTEGER_PACK) + +template <sz_t N> +using index_sequence_up_to = index_sequence<__integer_pack(N)...>; + +#elif defined(TINYVARIANT_USE_STD_INDEX_SEQUENCE) + +template <sz_t N> +using index_sequence_up_to = std::make_index_sequence<N>; + +#else + +#error "No integer sequence generation available." + +#endif + +template <typename, typename> +inline constexpr bool is_same_type = false; + +template <typename T> +inline constexpr bool is_same_type<T, T> = true; + +template <typename T> +[[nodiscard, gnu::always_inline]] constexpr T variadic_max( + std::initializer_list<T> il) noexcept +{ + T result = 0; + + for(T value : il) + { + if(value > result) + { + result = value; + } + } + + return result; +} + +template <sz_t N> +[[nodiscard, gnu::always_inline]] constexpr auto +smallest_int_type_for() noexcept +{ + if constexpr(N <= UINT8_MAX) + { + return std::uint8_t{}; + } + else if constexpr(N <= UINT16_MAX) + { + return std::uint16_t{}; + } + else if constexpr(N <= UINT32_MAX) + { + return std::uint32_t{}; + } + else if constexpr(N <= UINT64_MAX) + { + return std::uint64_t{}; + } + else + { + struct fail; + return fail{}; + } +} + +enum : sz_t +{ + bad_index = static_cast<sz_t>(-1) +}; + +template <typename T, typename... Ts> +[[nodiscard, gnu::always_inline]] constexpr sz_t index_of() noexcept +{ + constexpr bool matches[]{is_same_type<T, Ts>...}; + + for(sz_t i = 0; i < sizeof...(Ts); ++i) + { + if(matches[i]) + { + return i; + } + } + + return bad_index; +}; + +template <typename T> +struct type_wrapper +{ + using type = T; +}; + +template <sz_t N, typename T0 = void, typename T1 = void, typename T2 = void, + typename T3 = void, typename T4 = void, typename T5 = void, + typename T6 = void, typename T7 = void, typename T8 = void, + typename T9 = void, typename... Ts> +[[nodiscard, gnu::always_inline]] constexpr auto type_at_impl() noexcept +{ + // clang-format off + if constexpr(N == 0) { return type_wrapper<T0>{}; } + else if constexpr(N == 1) { return type_wrapper<T1>{}; } + else if constexpr(N == 2) { return type_wrapper<T2>{}; } + else if constexpr(N == 3) { return type_wrapper<T3>{}; } + else if constexpr(N == 4) { return type_wrapper<T4>{}; } + else if constexpr(N == 5) { return type_wrapper<T5>{}; } + else if constexpr(N == 6) { return type_wrapper<T6>{}; } + else if constexpr(N == 7) { return type_wrapper<T7>{}; } + else if constexpr(N == 8) { return type_wrapper<T8>{}; } + else if constexpr(N == 9) { return type_wrapper<T9>{}; } + else { return type_at_impl<N - 10, Ts...>(); } + // clang-format on +} + +template <sz_t N, typename... Ts> +using type_at = typename decltype(type_at_impl<N, Ts...>())::type; + +template <typename> +struct tinyvariant_inplace_type_t +{}; + +template <sz_t> +struct tinyvariant_inplace_index_t +{}; + +template <typename... Fs> +struct [[nodiscard]] overload_set : Fs... +{ + [[nodiscard, gnu::always_inline]] explicit overload_set(Fs&&... fs) noexcept + : Fs{static_cast<Fs&&>(fs)}... + {} + + using Fs::operator()...; +}; + +template <typename... Fs> +overload_set(Fs...) -> overload_set<Fs...>; + +template <typename T> +struct uncvref +{ + using type = T; +}; +template <typename T> +struct uncvref<T&> +{ + using type = T; +}; +template <typename T> +struct uncvref<T&&> +{ + using type = T; +}; +template <typename T> +struct uncvref<const T&> +{ + using type = T; +}; +template <typename T> +struct uncvref<const T&&> +{ + using type = T; +}; + +template <typename T> +using uncvref_t = typename uncvref<T>::type; + +template <typename T> +static constexpr bool is_reference = false; + +template <typename T> +static constexpr bool is_reference<T&> = true; + +template <typename T> +static constexpr bool is_reference<T&&> = true; + +struct void_type +{}; + +template <typename T> +struct regularize_void +{ + using type = T; +}; + +template <> +struct regularize_void<void> +{ + using type = void_type; +}; + +template <typename T> +using regularize_void_t = typename regularize_void<T>::type; + +template <typename... Ts> +using common_type_between_t = typename common_type_between<Ts...>::type; + +} // namespace vittorioromeo::impl + +namespace vittorioromeo { + +template <typename T> +inline constexpr impl::tinyvariant_inplace_type_t<T> tinyvariant_inplace_type{}; + +template <impl::sz_t N> +inline constexpr impl::tinyvariant_inplace_index_t<N> + tinyvariant_inplace_index{}; + +template <typename... Alternatives> +class [[nodiscard]] tinyvariant +{ +private: + using byte = unsigned char; + + enum : impl::sz_t + { + type_count = sizeof...(Alternatives), + max_alignment = impl::variadic_max({alignof(Alternatives)...}), + max_size = impl::variadic_max({sizeof(Alternatives)...}) + }; + + using index_type = decltype(impl::smallest_int_type_for<type_count>()); + + template <impl::sz_t I> + using nth_type = impl::type_at<I, Alternatives...>; + + template <typename T> + static constexpr impl::sz_t index_of = impl::index_of<T, Alternatives...>(); + + static constexpr impl::index_sequence_up_to<type_count> + alternative_index_sequence{}; + + alignas(max_alignment) byte _buffer[max_size]; + index_type _index; + +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 10) +#define TINYVARIANT_ALWAYS_INLINE_LAMBDA [[gnu::always_inline]] +#else +#define TINYVARIANT_ALWAYS_INLINE_LAMBDA +#endif + +#define TINYVARIANT_STATIC_ASSERT_INDEX_VALIDITY(I) \ + static_assert( \ + (I) != impl::bad_index, "Alternative type not supported by variant"); \ + \ + static_assert( \ + (I) >= 0 && (I) < type_count, "Alternative index out of range") + +#define TINYVARIANT_DO_WITH_CURRENT_INDEX_OBJ(obj, Is, ...) \ + do \ + { \ + if constexpr(sizeof...(Alternatives) == 1) \ + { \ + if(constexpr impl::sz_t Is = 0; (obj)._index == Is) \ + { \ + __VA_ARGS__; \ + } \ + } \ + else if constexpr(sizeof...(Alternatives) == 2) \ + { \ + if(constexpr impl::sz_t Is = 0; (obj)._index == Is) \ + { \ + __VA_ARGS__; \ + } \ + else if(constexpr impl::sz_t Is = 1; (obj)._index == Is) \ + { \ + __VA_ARGS__; \ + } \ + } \ + else \ + { \ + [&]<impl::sz_t... Is>(impl::index_sequence<Is...>) \ + TINYVARIANT_ALWAYS_INLINE_LAMBDA { \ + ((((obj)._index == Is) ? ((__VA_ARGS__), 0) : 0), ...); \ + }(alternative_index_sequence); \ + } \ + } \ + while(false) + +#define TINYVARIANT_DO_WITH_CURRENT_INDEX(Is, ...) \ + TINYVARIANT_DO_WITH_CURRENT_INDEX_OBJ((*this), Is, __VA_ARGS__) + + template <typename T, impl::sz_t I, typename... Args> + [[nodiscard, gnu::always_inline]] explicit tinyvariant( + impl::tinyvariant_inplace_type_t<T>, + impl::tinyvariant_inplace_index_t<I>, Args&&... args) noexcept + : _index{static_cast<index_type>(I)} + { + TINYVARIANT_STATIC_ASSERT_INDEX_VALIDITY(I); + new(_buffer) T{static_cast<Args&&>(args)...}; + } + + template <impl::sz_t I> + [[gnu::always_inline]] void destroy_at() noexcept + { + as<nth_type<I>>().~nth_type<I>(); + } + + template <impl::sz_t I, typename R, typename Visitor> + [[nodiscard, gnu::always_inline]] R recursive_visit_impl(Visitor&& visitor) + { + if constexpr(I < sizeof...(Alternatives) - 1) + { + return (_index == I) ? visitor(get_by_index<I>()) + : recursive_visit_impl<I + 1, R>( + static_cast<Visitor&&>(visitor)); + } + else + { + return visitor(get_by_index<I>()); + } + } + + template <impl::sz_t I, typename R, typename Visitor> + [[nodiscard, gnu::always_inline]] R recursive_visit_opt5_impl( + Visitor&& visitor) + { + if constexpr(I == 0 && sizeof...(Alternatives) == 5) + { + // clang-format off + return (_index == I + 0) ? visitor(get_by_index<I + 0>()) : + (_index == I + 1) ? visitor(get_by_index<I + 1>()) : + (_index == I + 2) ? visitor(get_by_index<I + 2>()) : + (_index == I + 3) ? visitor(get_by_index<I + 3>()) : + visitor(get_by_index<I + 4>()) ; + // clang-format on + } + else if constexpr(I + 4 < sizeof...(Alternatives)) + { + // clang-format off + return (_index == I + 0) ? visitor(get_by_index<I + 0>()) : + (_index == I + 1) ? visitor(get_by_index<I + 1>()) : + (_index == I + 2) ? visitor(get_by_index<I + 2>()) : + (_index == I + 3) ? visitor(get_by_index<I + 3>()) : + (_index == I + 4) ? visitor(get_by_index<I + 4>()) : + recursive_visit_opt5_impl<I + 5, R>(static_cast<Visitor&&>(visitor)); + // clang-format on + } + else + { + return recursive_visit_impl<I, R>(static_cast<Visitor&&>(visitor)); + } + } + + template <impl::sz_t I, typename R, typename Visitor> + [[nodiscard, gnu::always_inline]] R recursive_visit_opt10_impl( + Visitor&& visitor) + { + if constexpr(I + 9 < sizeof...(Alternatives)) + { + // clang-format off + return (_index == I + 0) ? visitor(get_by_index<I + 0>()) : + (_index == I + 1) ? visitor(get_by_index<I + 1>()) : + (_index == I + 2) ? visitor(get_by_index<I + 2>()) : + (_index == I + 3) ? visitor(get_by_index<I + 3>()) : + (_index == I + 4) ? visitor(get_by_index<I + 4>()) : + (_index == I + 5) ? visitor(get_by_index<I + 5>()) : + (_index == I + 6) ? visitor(get_by_index<I + 6>()) : + (_index == I + 7) ? visitor(get_by_index<I + 7>()) : + (_index == I + 8) ? visitor(get_by_index<I + 8>()) : + (_index == I + 9) ? visitor(get_by_index<I + 9>()) : + recursive_visit_opt10_impl<I + 10, R>(static_cast<Visitor&&>(visitor)); + // clang-format on + } + else + { + return recursive_visit_opt5_impl<I, R>( + static_cast<Visitor&&>(visitor)); + } + } + +public: + template <typename T, typename... Args> + [[nodiscard, gnu::always_inline]] explicit tinyvariant( + impl::tinyvariant_inplace_type_t<T> inplace_type, + Args&&... args) noexcept + : tinyvariant{inplace_type, tinyvariant_inplace_index<index_of<T>>, + static_cast<Args&&>(args)...} + {} + + template <impl::sz_t I, typename... Args> + [[nodiscard, gnu::always_inline]] explicit tinyvariant( + impl::tinyvariant_inplace_index_t<I> inplace_index, + Args&&... args) noexcept + : tinyvariant{tinyvariant_inplace_type<nth_type<I>>, inplace_index, + static_cast<Args&&>(args)...} + {} + + template <typename T> + [[nodiscard, gnu::always_inline]] explicit tinyvariant(T&& x) noexcept + : tinyvariant{tinyvariant_inplace_type<T>, static_cast<T&&>(x)} + {} + + [[nodiscard, gnu::always_inline]] explicit tinyvariant() noexcept + : tinyvariant{tinyvariant_inplace_index<0>} + {} + + [[gnu::always_inline]] tinyvariant(const tinyvariant& rhs) + : _index{rhs._index} + { + TINYVARIANT_DO_WITH_CURRENT_INDEX( + I, new(_buffer) nth_type<I>(static_cast<const nth_type<I>&>( + *reinterpret_cast<const nth_type<I>*>(rhs._buffer)))); + } + + [[gnu::always_inline]] tinyvariant(tinyvariant&& rhs) noexcept + : _index{rhs._index} + { + TINYVARIANT_DO_WITH_CURRENT_INDEX( + I, new(_buffer) nth_type<I>(static_cast<nth_type<I>&&>( + *reinterpret_cast<nth_type<I>*>(rhs._buffer)))); + } + + // Avoid forwarding constructor hijack. + [[gnu::always_inline]] tinyvariant(const tinyvariant&& rhs) noexcept + : tinyvariant{static_cast<const tinyvariant&>(rhs)} + {} + + // Avoid forwarding constructor hijack. + [[gnu::always_inline]] tinyvariant(tinyvariant& rhs) + : tinyvariant{static_cast<const tinyvariant&>(rhs)} + {} + + [[gnu::always_inline]] ~tinyvariant() + { + TINYVARIANT_DO_WITH_CURRENT_INDEX(I, destroy_at<I>()); + } + + [[gnu::always_inline]] tinyvariant& operator=(const tinyvariant& rhs) + { + if(this == &rhs) + { + return *this; + } + + TINYVARIANT_DO_WITH_CURRENT_INDEX(I, destroy_at<I>()); + + TINYVARIANT_DO_WITH_CURRENT_INDEX_OBJ( + rhs, I, (new(_buffer) nth_type<I>(rhs.template as<nth_type<I>>()))); + _index = rhs._index; + + return *this; + } + + [[gnu::always_inline]] tinyvariant& operator=(tinyvariant& rhs) + { + return ((*this) = static_cast<const tinyvariant&>(rhs)); + } + + [[gnu::always_inline]] tinyvariant& operator=(tinyvariant&& rhs) noexcept + { + TINYVARIANT_DO_WITH_CURRENT_INDEX(I, destroy_at<I>()); + + TINYVARIANT_DO_WITH_CURRENT_INDEX_OBJ(rhs, I, + (new(_buffer) nth_type<I>( + static_cast<nth_type<I>&&>(rhs.template as<nth_type<I>>())))); + _index = rhs._index; + + return *this; + } + + [[gnu::always_inline]] tinyvariant& operator=(const tinyvariant&& rhs) + { + return ((*this) = static_cast<const tinyvariant&>(rhs)); + } + + template <typename T> + [[gnu::always_inline]] tinyvariant& operator=(T&& x) + { + TINYVARIANT_DO_WITH_CURRENT_INDEX(I, destroy_at<I>()); + + using type = impl::uncvref_t<T>; + + new(_buffer) type{static_cast<T&&>(x)}; + _index = index_of<type>; + + return *this; + } + + template <typename T> + [[nodiscard, gnu::always_inline]] bool is() const noexcept + { + return _index == index_of<T>; + } + + [[nodiscard, gnu::always_inline]] bool has_index( + index_type index) const noexcept + { + return _index == index; + } + + template <typename T> + [[nodiscard, gnu::always_inline]] T& as() & noexcept + { + return *(reinterpret_cast<T*>(_buffer)); + } + + template <typename T> + [[nodiscard, gnu::always_inline]] const T& as() const& noexcept + { + return *(reinterpret_cast<const T*>(_buffer)); + } + + template <typename T> + [[nodiscard, gnu::always_inline]] T&& as() && noexcept + { + return static_cast<T&&>(*(reinterpret_cast<T*>(_buffer))); + } + + template <impl::sz_t I> + [[nodiscard, gnu::always_inline]] auto& get_by_index() & noexcept + { + TINYVARIANT_STATIC_ASSERT_INDEX_VALIDITY(I); + return as<nth_type<I>>(); + } + + template <impl::sz_t I> + [[nodiscard, gnu::always_inline]] const auto& get_by_index() const& noexcept + { + TINYVARIANT_STATIC_ASSERT_INDEX_VALIDITY(I); + return as<nth_type<I>>(); + } + + template <impl::sz_t I> + [[nodiscard, gnu::always_inline]] auto&& get_by_index() && noexcept + { + TINYVARIANT_STATIC_ASSERT_INDEX_VALIDITY(I); + return static_cast<nth_type<I>&&>(as<nth_type<I>>()); + } + + template <typename Visitor, typename R = decltype(impl::declval<Visitor>()( + impl::declval<nth_type<0>>()))> + [[nodiscard, gnu::always_inline]] R recursive_visit(Visitor&& visitor) & + { + if constexpr(sizeof...(Alternatives) >= 10) + { + return recursive_visit_opt10_impl<0, R>( + static_cast<Visitor&&>(visitor)); + } + else if constexpr(sizeof...(Alternatives) >= 5) + { + return recursive_visit_opt5_impl<0, R>( + static_cast<Visitor&&>(visitor)); + } + else + { + return recursive_visit_impl<0, R>(static_cast<Visitor&&>(visitor)); + } + } + + template <typename Visitor, typename R = decltype(impl::declval<Visitor>()( + impl::declval<nth_type<0>>()))> + [[nodiscard, gnu::always_inline]] R recursive_visit( + Visitor&& visitor) const& + { + if constexpr(sizeof...(Alternatives) >= 10) + { + return recursive_visit_opt10_impl<0, R>( + static_cast<Visitor&&>(visitor)); + } + else if constexpr(sizeof...(Alternatives) >= 5) + { + return recursive_visit_opt5_impl<0, R>( + static_cast<Visitor&&>(visitor)); + } + else + { + return recursive_visit_impl<0, R>(static_cast<Visitor&&>(visitor)); + } + } + + template <typename... Fs> + [[nodiscard, gnu::always_inline]] auto recursive_match( + Fs&&... fs) & -> decltype(recursive_visit(impl::overload_set{ + static_cast<Fs&&>(fs)...})) + { + return recursive_visit(impl::overload_set{static_cast<Fs&&>(fs)...}); + } + + template <typename... Fs> + [[nodiscard, gnu::always_inline]] auto recursive_match( + Fs&&... fs) const& -> decltype(recursive_visit(impl::overload_set{ + static_cast<Fs&&>(fs)...})) + { + return recursive_visit(impl::overload_set{static_cast<Fs&&>(fs)...}); + } + + template <typename Visitor, typename R = decltype(impl::declval<Visitor>()( + impl::declval<nth_type<0>>()))> + [[nodiscard, gnu::always_inline]] R linear_visit(Visitor&& visitor) & + { + if constexpr(impl::is_reference<R>) + { + impl::uncvref_t<R>* ret; + TINYVARIANT_DO_WITH_CURRENT_INDEX( + I, ret = &(visitor(get_by_index<I>()))); + return static_cast<R>(*ret); + } + else + { + alignas(R) byte ret_buffer[sizeof(R)]; + + TINYVARIANT_DO_WITH_CURRENT_INDEX( + I, new(ret_buffer) R(visitor(get_by_index<I>()))); + + return *(reinterpret_cast<R*>(ret_buffer)); + } + } + + + template <typename Visitor, typename R = decltype(impl::declval<Visitor>()( + impl::declval<nth_type<0>>()))> + [[nodiscard, gnu::always_inline]] R linear_visit(Visitor&& visitor) const& + { + if constexpr(impl::is_reference<R>) + { + impl::uncvref_t<R>* ret; + TINYVARIANT_DO_WITH_CURRENT_INDEX( + I, ret = &(visitor(get_by_index<I>()))); + return static_cast<R>(*ret); + } + else + { + alignas(R) byte ret_buffer[sizeof(R)]; + + TINYVARIANT_DO_WITH_CURRENT_INDEX( + I, new(ret_buffer) R(visitor(get_by_index<I>()))); + + return *(reinterpret_cast<R*>(ret_buffer)); + } + } + + template <typename... Fs> + [[nodiscard, gnu::always_inline]] auto linear_match( + Fs&&... fs) & -> decltype(linear_visit(impl::overload_set{ + static_cast<Fs&&>(fs)...})) + { + return linear_visit(impl::overload_set{static_cast<Fs&&>(fs)...}); + } + + template <typename... Fs> + [[nodiscard, gnu::always_inline]] auto linear_match( + Fs&&... fs) const& -> decltype(linear_visit(impl::overload_set{ + static_cast<Fs&&>(fs)...})) + { + return linear_visit(impl::overload_set{static_cast<Fs&&>(fs)...}); + } +}; + +#undef TINYVARIANT_DO_WITH_CURRENT_INDEX +#undef TINYVARIANT_STATIC_ASSERT_INDEX_VALIDITY + +#undef TINYVARIANT_USE_STD_INDEX_SEQUENCE +#undef TINYVARIANT_USE_INTEGER_PACK +#undef TINYVARIANT_USE_MAKE_INTEGER_SEQ + +} // namespace vittorioromeo diff --git a/include/SSVOpenHexagon/Utils/Utils.hpp b/include/SSVOpenHexagon/Utils/Utils.hpp index f638226a..ce10a726 100644 --- a/include/SSVOpenHexagon/Utils/Utils.hpp +++ b/include/SSVOpenHexagon/Utils/Utils.hpp @@ -38,8 +38,9 @@ void shakeCamera( ssvu::TimelineManager& mTimelineManager, ssvs::Camera& mCamera); void runLuaCode(Lua::LuaContext& mLua, const std::string& mCode); - void runLuaFile(Lua::LuaContext& mLua, const std::string& mFileName); +bool runLuaFileCached( + HGAssets& assets, Lua::LuaContext& mLua, const std::string& mFileName); struct Nothing {}; @@ -49,14 +50,14 @@ using VoidToNothing = std::conditional_t<std::is_same_v<T, void>, Nothing, T>; template <typename T, typename... TArgs> T runLuaFunction( - Lua::LuaContext& mLua, const std::string& mName, const TArgs&... mArgs) + Lua::LuaContext& mLua, std::string_view mName, const TArgs&... mArgs) { return mLua.callLuaFunction<T>(mName, std::make_tuple(mArgs...)); } template <typename T, typename... TArgs> auto runLuaFunctionIfExists( - Lua::LuaContext& mLua, const std::string& mName, const TArgs&... mArgs) + Lua::LuaContext& mLua, std::string_view mName, const TArgs&... mArgs) { using Ret = std::optional<VoidToNothing<T>>; diff --git a/prepare_release.sh b/prepare_release.sh index da6e28e4..e25b74b4 100644 --- a/prepare_release.sh +++ b/prepare_release.sh @@ -19,25 +19,15 @@ mkdir -p ./_PREPARED_RELEASE/Profiles cp ./_RELEASE/SSVOpenHexagon.exe ./_PREPARED_RELEASE cp ./_RELEASE/SSVOpenHexagon-Console.exe ./_PREPARED_RELEASE -cp ./_RELEASE/libzlib1.dll ./_PREPARED_RELEASE -cp ./_RELEASE/openal32.dll ./_PREPARED_RELEASE -cp ./_RELEASE/sfml-audio-3.dll ./_PREPARED_RELEASE -cp ./_RELEASE/sfml-graphics-3.dll ./_PREPARED_RELEASE -cp ./_RELEASE/sfml-network-3.dll ./_PREPARED_RELEASE -cp ./_RELEASE/sfml-system-3.dll ./_PREPARED_RELEASE -cp ./_RELEASE/sfml-window-3.dll ./_PREPARED_RELEASE -cp ./_RELEASE/libImGui-SFML.dll ./_PREPARED_RELEASE -cp ./_RELEASE/libluajit.dll ./_PREPARED_RELEASE +cp ./_RELEASE/libzlib.dll ./_PREPARED_RELEASE +cp ./_RELEASE/libopenal-1.dll ./_PREPARED_RELEASE cp ./_RELEASE/steam_api64.dll ./_PREPARED_RELEASE cp ./_RELEASE/discord_game_sdk.dll ./_PREPARED_RELEASE -cp ./_RELEASE/libsodium.dll ./_PREPARED_RELEASE cp ./_RELEASE/sdkencryptedappticket64.dll ./_PREPARED_RELEASE - -cp ./_RELEASE/libssp-0.dll ./_PREPARED_RELEASE cp ./_RELEASE/libstdc++-6.dll ./_PREPARED_RELEASE cp ./_RELEASE/libgcc_s_seh-1.dll ./_PREPARED_RELEASE cp ./_RELEASE/libwinpthread-1.dll ./_PREPARED_RELEASE -cp ./_RELEASE/libopenal-1.dll ./_PREPARED_RELEASE +cp ./_RELEASE/libssp-0.dll ./_PREPARED_RELEASE cp ./_RELEASE/steam_appid.txt ./_PREPARED_RELEASE cp ./_RELEASE/windowed.bat ./_PREPARED_RELEASE @@ -50,8 +40,20 @@ cp ./_RELEASE/noaudio.bat ./_PREPARED_RELEASE cp ./_RELEASE/OHWorkshopUploader.exe ./_PREPARED_RELEASE cd ./_PREPARED_RELEASE -upx -9 ./*.dll -upx -9 ./*.exe + +echo "PACKING WITH UPX" + +for x in ./*.dll; do + upx -9 $x & +done + +for x in ./*.exe; do + upx -9 $x & +done + +wait +echo "DONE PACKING WITH UPX" + cd .. cp -r ./_PREPARED_RELEASE ./_PREPARED_RELEASE_TEST diff --git a/prepare_release_linux.sh b/prepare_release_linux.sh index 29fc7f76..ed81fef4 100755 --- a/prepare_release_linux.sh +++ b/prepare_release_linux.sh @@ -19,29 +19,36 @@ mkdir -p ./_PREPARED_RELEASE_LINUX/Profiles cp ./_RELEASE/SSVOpenHexagonLinux ./_PREPARED_RELEASE_LINUX cp ./_RELEASE/OHWorkshopUploaderLinux ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libImGui-SFML.so ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libdiscord_game_sdk.so ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libluajit.so ./_PREPARED_RELEASE_LINUX cp ./_RELEASE/libsdkencryptedappticket.so ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libsfml-audio.so.3.0 ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libsfml-graphics.so.3.0 ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libsfml-network.so.3.0 ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libsfml-system.so.3.0 ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libsfml-window.so.3.0 ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libsodium.so ./_PREPARED_RELEASE_LINUX -cp ./_RELEASE/libstdc++.so.6 ./_PREPARED_RELEASE_LINUX +cp ./_RELEASE/libdiscord_game_sdk.so ./_PREPARED_RELEASE_LINUX cp ./_RELEASE/libsteam_api.so ./_PREPARED_RELEASE_LINUX cp ./_RELEASE/libz.so.1 ./_PREPARED_RELEASE_LINUX +cp /lib/x86_64-linux-gnu/libopenal.so.1 ./_PREPARED_RELEASE_LINUX +cp /lib/x86_64-linux-gnu/libFLAC.so.8 ./_PREPARED_RELEASE_LINUX +cp /lib/x86_64-linux-gnu/libsndio.so.7 ./_PREPARED_RELEASE_LINUX cp ./_RELEASE/steam_appid.txt ./_PREPARED_RELEASE_LINUX cp ./_RELEASE/run_ssvopenhexagon_linux.sh ./_PREPARED_RELEASE_LINUX cd ./_PREPARED_RELEASE_LINUX + chmod +x ./*.so -upx -9 ./*.so -upx -9 ./SSVOpenHexagonLinux -upx -9 ./OHWorkshopUploaderLinux +chmod +x ./SSVOpenHexagonLinux +chmod +x ./OHWorkshopUploaderLinux + +# echo "PACKING WITH UPX" +# +# for x in ./*.so; do +# upx -9 $x & +# done +# +# upx -9 ./SSVOpenHexagonLinux & +# upx -9 ./OHWorkshopUploaderLinux & +# +# wait +# echo "DONE PACKING WITH UPX" + cd .. cp -r ./_PREPARED_RELEASE_LINUX ./_PREPARED_RELEASE_LINUX_TEST diff --git a/public/sqlite_orm/sqlite_orm.h b/public/sqlite_orm/sqlite_orm.h index 67df39aa..e5135a2c 100644 --- a/public/sqlite_orm/sqlite_orm.h +++ b/public/sqlite_orm/sqlite_orm.h @@ -1,35 +1,352 @@ #pragma once #if defined(_MSC_VER) -#if defined(min) __pragma(push_macro("min")) #undef min -#define __RESTORE_MIN__ -#endif -#if defined(max) - __pragma(push_macro("max")) +__pragma(push_macro("max")) #undef max -#define __RESTORE_MAX__ -#endif #endif // defined(_MSC_VER) +#pragma once -#include <ciso646> // due to #166 +#include <type_traits> // std::enable_if, std::is_same -#if __cplusplus >= 201703L // use of C++17 or higher -// Enables use of std::optional in SQLITE_ORM. -#define SQLITE_ORM_OPTIONAL_SUPPORTED -#define SQLITE_ORM_STRING_VIEW_SUPPORTED +// #include "functional/cxx_type_traits_polyfill.h" + +#include <type_traits> + +// #include "cxx_universal.h" + +/* + * This header makes central C++ functionality on which sqlite_orm depends universally available: + * - alternative operator representations + * - ::size_t, ::ptrdiff_t, ::nullptr_t + * - C++ core feature macros + * - macros for dealing with compiler quirks + */ + +#include <iso646.h> // alternative operator representations +#include <cstddef> // sqlite_orm is using size_t, ptrdiff_t, nullptr_t everywhere, pull it in early + +// earlier clang versions didn't make nullptr_t available in the global namespace via stddef.h, +// though it should have according to C++ documentation (see https://en.cppreference.com/w/cpp/types/nullptr_t#Notes). +// actually it should be available when including stddef.h +using std::nullptr_t; + +// #include "cxx_core_features.h" + +#ifdef __has_cpp_attribute +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define SQLITE_ORM_HAS_CPP_ATTRIBUTE(attr) 0L +#endif + +#ifdef __has_include +#define SQLITE_ORM_HAS_INCLUDE(file) __has_include(file) +#else +#define SQLITE_ORM_HAS_INCLUDE(file) 0L +#endif + +#if __cpp_aggregate_nsdmi >= 201304L +#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED +#endif + +#if __cpp_constexpr >= 201304L +#define SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_noexcept_function_type >= 201510L #define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED #endif + +#if __cpp_aggregate_bases >= 201603L +#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED +#endif + +#if __cpp_fold_expressions >= 201603L +#define SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#endif + +#if __cpp_if_constexpr >= 201606L +#define SQLITE_ORM_IF_CONSTEXPR_SUPPORTED +#endif + +#if __cpp_inline_variables >= 201606L +#define SQLITE_ORM_INLINE_VAR inline +#else +#define SQLITE_ORM_INLINE_VAR +#endif + +#if __cpp_generic_lambdas >= 201707L +#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED +#else +#endif + +#if SQLITE_ORM_HAS_CPP_ATTRIBUTE(no_unique_address) >= 201803L +#define SQLITE_ORM_NOUNIQUEADDRESS [[no_unique_address]] +#else +#define SQLITE_ORM_NOUNIQUEADDRESS +#endif + +#if __cpp_consteval >= 201811L +#define SQLITE_ORM_CONSTEVAL consteval +#else +#define SQLITE_ORM_CONSTEVAL constexpr +#endif + +#if __cpp_aggregate_paren_init >= 201902L +#define SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED +#endif + +#if __cpp_concepts >= 201907L +#define SQLITE_ORM_CONCEPTS_SUPPORTED +#endif + +// #include "cxx_compiler_quirks.h" + +#ifdef __clang__ +#define SQLITE_ORM_DO_PRAGMA(...) _Pragma(#__VA_ARGS__) +#endif + +#ifdef __clang__ +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic push) \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic ignored warnoption) \ + __VA_ARGS__ \ + SQLITE_ORM_DO_PRAGMA(clang diagnostic pop) + +#else +#define SQLITE_ORM_CLANG_SUPPRESS(warnoption, ...) __VA_ARGS__ +#endif + +// clang has the bad habit of diagnosing missing brace-init-lists when constructing aggregates with base classes. +// This is a false positive, since the C++ standard is quite clear that braces for nested or base objects may be omitted, +// see https://en.cppreference.com/w/cpp/language/aggregate_initialization: +// "The braces around the nested initializer lists may be elided (omitted), +// in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, +// and the subsequent initializer clauses are used to initialize the following members of the object." +// In this sense clang should only warn about missing field initializers. +// Because we know what we are doing, we suppress the diagnostic message +#define SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(...) SQLITE_ORM_CLANG_SUPPRESS("-Wmissing-braces", __VA_ARGS__) + +#if defined(_MSC_VER) && (_MSC_VER < 1920) +#define SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION +#endif + +namespace sqlite_orm { + namespace internal { + namespace polyfill { +#if __cpp_lib_void_t >= 201411L + using std::void_t; +#else + template<class...> + using void_t = void; +#endif + +#if __cpp_lib_bool_constant >= 201505L + using std::bool_constant; +#else + template<bool v> + using bool_constant = std::integral_constant<bool, v>; +#endif + +#if __cpp_lib_logical_traits >= 201510L && __cpp_lib_type_trait_variable_templates >= 201510L + using std::conjunction; + using std::conjunction_v; + using std::disjunction; + using std::disjunction_v; + using std::negation; + using std::negation_v; +#else + template<typename...> + struct conjunction : std::true_type {}; + template<typename B1> + struct conjunction<B1> : B1 {}; + template<typename B1, typename... Bn> + struct conjunction<B1, Bn...> : std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {}; + template<typename... Bs> + SQLITE_ORM_INLINE_VAR constexpr bool conjunction_v = conjunction<Bs...>::value; + + template<typename...> + struct disjunction : std::false_type {}; + template<typename B1> + struct disjunction<B1> : B1 {}; + template<typename B1, typename... Bn> + struct disjunction<B1, Bn...> : std::conditional_t<bool(B1::value), B1, disjunction<Bn...>> {}; + template<typename... Bs> + SQLITE_ORM_INLINE_VAR constexpr bool disjunction_v = disjunction<Bs...>::value; + + template<typename B> + struct negation : bool_constant<!bool(B::value)> {}; + template<typename B> + SQLITE_ORM_INLINE_VAR constexpr bool negation_v = negation<B>::value; +#endif + +#if __cpp_lib_remove_cvref >= 201711L + using std::remove_cvref, std::remove_cvref_t; +#else + template<class T> + struct remove_cvref : std::remove_cv<std::remove_reference_t<T>> {}; + + template<class T> + using remove_cvref_t = typename remove_cvref<T>::type; +#endif + +#if __cpp_lib_type_identity >= 201806L + using std::type_identity, std::type_identity_t; +#else + template<class T> + struct type_identity { + using type = T; + }; + + template<class T> + using type_identity_t = typename type_identity<T>::type; +#endif + +#if 0 // __cpp_lib_detect >= 0L // library fundamentals TS v2, [meta.detect] + using std::nonesuch; + using std::detector; + using std::is_detected, std::is_detected_v; + using std::detected, std::detected_t; + using std::detected_or, std::detected_or_t; +#else + struct nonesuch { + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + template<class Default, class AlwaysVoid, template<class...> class Op, class... Args> + struct detector { + using value_t = std::false_type; + using type = Default; + }; + + template<class Default, template<class...> class Op, class... Args> + struct detector<Default, polyfill::void_t<Op<Args...>>, Op, Args...> { + using value_t = std::true_type; + using type = Op<Args...>; + }; + + template<template<class...> class Op, class... Args> + using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t; + + template<template<class...> class Op, class... Args> + using detected = detector<nonesuch, void, Op, Args...>; + + template<template<class...> class Op, class... Args> + using detected_t = typename detector<nonesuch, void, Op, Args...>::type; + + template<class Default, template<class...> class Op, class... Args> + using detected_or = detector<Default, void, Op, Args...>; + + template<class Default, template<class...> class Op, class... Args> + using detected_or_t = typename detected_or<Default, Op, Args...>::type; + + template<template<class...> class Op, class... Args> + SQLITE_ORM_INLINE_VAR constexpr bool is_detected_v = is_detected<Op, Args...>::value; +#endif + +#if 0 // proposed but not pursued + using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v; +#else + // is_specialization_of: https://github.com/cplusplus/papers/issues/812 + + template<typename Type, template<typename...> class Primary> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v = false; + + template<template<typename...> class Primary, class... Types> + SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v<Primary<Types...>, Primary> = true; + + template<typename Type, template<typename...> class Primary> + struct is_specialization_of : bool_constant<is_specialization_of_v<Type, Primary>> {}; +#endif + + template<typename...> + SQLITE_ORM_INLINE_VAR constexpr bool always_false_v = false; + + template<size_t I> + using index_constant = std::integral_constant<size_t, I>; + } + } + + namespace polyfill = internal::polyfill; +} + +namespace sqlite_orm { + // C++ generic traits used throughout the library + namespace internal { + template<class T, class... Types> + using is_any_of = polyfill::disjunction<std::is_same<T, Types>...>; + + // enable_if for types + template<template<typename...> class Op, class... Args> + using match_if = std::enable_if_t<Op<Args...>::value>; + + // enable_if for types + template<template<typename...> class Op, class... Args> + using match_if_not = std::enable_if_t<polyfill::negation_v<Op<Args...>>>; + + // enable_if for types + template<class T, template<typename...> class Primary> + using match_specialization_of = std::enable_if_t<polyfill::is_specialization_of_v<T, Primary>>; + + // enable_if for functions + template<template<typename...> class Op, class... Args> + using satisfies = std::enable_if_t<Op<Args...>::value, bool>; + + // enable_if for functions + template<template<typename...> class Op, class... Args> + using satisfies_not = std::enable_if_t<polyfill::negation<Op<Args...>>::value, bool>; + + // enable_if for functions + template<class T, template<typename...> class Primary> + using satisfies_is_specialization_of = std::enable_if_t<polyfill::is_specialization_of_v<T, Primary>, bool>; + } + + // type name template aliases for syntactic sugar + namespace internal { + template<typename T> + using type_t = typename T::type; + + template<typename T> + using field_type_t = typename T::field_type; + + template<typename T> + using constraints_type_t = typename T::constraints_type; + + template<typename T> + using object_type_t = typename T::object_type; + + template<typename T> + using elements_type_t = typename T::elements_type; + + template<typename T> + using target_type_t = typename T::target_type; + } +} #pragma once +#include <sqlite3.h> #include <system_error> // std::error_code, std::system_error #include <string> // std::string -#include <sqlite3.h> #include <stdexcept> #include <sstream> // std::ostringstream +#include <type_traits> - namespace sqlite_orm { +namespace sqlite_orm { + + /** @short Enables classifying sqlite error codes. + + @note We don't bother listing all possible values; + this also allows for compatibility with + 'Construction rules for enum class values (P0138R2)' + */ + enum class sqlite_errc {}; enum class orm_error_code { not_found = 1, @@ -50,7 +367,18 @@ __pragma(push_macro("min")) arguments_count_does_not_match, function_not_found, index_is_out_of_bounds, + value_is_null, + no_tables_specified, }; + +} + +namespace std { + template<> + struct is_error_code_enum<::sqlite_orm::sqlite_errc> : true_type {}; + + template<> + struct is_error_code_enum<::sqlite_orm::orm_error_code> : true_type {}; } namespace sqlite_orm { @@ -97,6 +425,10 @@ namespace sqlite_orm { return "Function not found"; case orm_error_code::index_is_out_of_bounds: return "Index is out of bounds"; + case orm_error_code::value_is_null: + return "Value is null"; + case orm_error_code::no_tables_specified: + return "No tables specified"; default: return "unknown error"; } @@ -124,6 +456,14 @@ namespace sqlite_orm { return res; } + inline std::error_code make_error_code(sqlite_errc ev) noexcept { + return {static_cast<int>(ev), get_sqlite_error_category()}; + } + + inline std::error_code make_error_code(orm_error_code ev) noexcept { + return {static_cast<int>(ev), get_orm_error_category()}; + } + template<typename... T> std::string get_error_message(sqlite3* db, T&&... args) { std::ostringstream stream; @@ -135,454 +475,151 @@ namespace sqlite_orm { template<typename... T> [[noreturn]] void throw_error(sqlite3* db, T&&... args) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - get_error_message(db, std::forward<T>(args)...)); + throw std::system_error{sqlite_errc(sqlite3_errcode(db)), get_error_message(db, std::forward<T>(args)...)}; } -} - -namespace std { - template<> - struct is_error_code_enum<sqlite_orm::orm_error_code> : std::true_type {}; - inline std::error_code make_error_code(sqlite_orm::orm_error_code errorCode) { - return std::error_code(static_cast<int>(errorCode), sqlite_orm::get_orm_error_category()); + inline std::system_error sqlite_to_system_error(int ev) { + return {sqlite_errc(ev)}; } -} -#pragma once -#include <tuple> // std::tuple, std::get, std::tuple_element, std::tuple_size -#include <type_traits> // std::false_type, std::true_type - -// #include "static_magic.h" - -#include <type_traits> // std::false_type, std::true_type, std::integral_constant - -namespace sqlite_orm { - - // got from here - // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co - namespace internal { - - static inline decltype(auto) empty_callable() { - static auto res = [](auto&&...) {}; - return (res); - } - - template<typename T, typename F> - decltype(auto) static_if(std::true_type, const T& t, const F&) { - return (t); - } - - template<typename T, typename F> - decltype(auto) static_if(std::false_type, const T&, const F& f) { - return (f); - } - - template<bool B, typename T, typename F> - decltype(auto) static_if(const T& t, const F& f) { - return static_if(std::integral_constant<bool, B>{}, t, f); - } - - template<bool B, typename T> - decltype(auto) static_if(const T& t) { - return static_if(std::integral_constant<bool, B>{}, t, empty_callable()); - } - - template<typename T> - using static_not = std::integral_constant<bool, !T::value>; + inline std::system_error sqlite_to_system_error(sqlite3* db) { + return {sqlite_errc(sqlite3_errcode(db)), sqlite3_errmsg(db)}; } -} - -namespace sqlite_orm { - - // got from here http://stackoverflow.com/questions/25958259/how-do-i-find-out-if-a-tuple-contains-a-type - namespace tuple_helper { - - /** - * HAS_TYPE type trait - */ - template<typename T, typename Tuple> - struct has_type; - - template<typename T> - struct has_type<T, std::tuple<>> : std::false_type {}; - - template<typename T, typename U, typename... Ts> - struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>> {}; - - template<typename T, typename... Ts> - struct has_type<T, std::tuple<T, Ts...>> : std::true_type {}; - - template<typename T, typename Tuple> - using tuple_contains_type = typename has_type<T, Tuple>::type; - - /** - * HAS_SOME_TYPE type trait - */ - template<template<class> class TT, typename Tuple> - struct has_some_type; - - template<template<class> class TT> - struct has_some_type<TT, std::tuple<>> : std::false_type {}; - - template<template<class> class TT, typename U, typename... Ts> - struct has_some_type<TT, std::tuple<U, Ts...>> : has_some_type<TT, std::tuple<Ts...>> {}; - - template<template<class> class TT, typename T, typename... Ts> - struct has_some_type<TT, std::tuple<TT<T>, Ts...>> : std::true_type {}; - - template<template<class> class TT, typename Tuple> - using tuple_contains_some_type = typename has_some_type<TT, Tuple>::type; - - template<size_t N, class... Args> - struct iterator_impl { - - template<class L> - void operator()(const std::tuple<Args...>& tuple, const L& lambda, bool reverse = true) { - if(reverse) { - lambda(std::get<N>(tuple)); - iterator_impl<N - 1, Args...>()(tuple, lambda, reverse); - } else { - iterator_impl<N - 1, Args...>()(tuple, lambda, reverse); - lambda(std::get<N>(tuple)); - } - } - - template<class L> - void operator()(const L& lambda) { - iterator_impl<N - 1, Args...>()(lambda); - lambda((const typename std::tuple_element<N - 1, std::tuple<Args...>>::type*)nullptr); - } - }; - - template<class... Args> - struct iterator_impl<0, Args...> { - - template<class L> - void operator()(const std::tuple<Args...>& tuple, const L& lambda, bool /*reverse*/ = true) { - lambda(std::get<0>(tuple)); - } - - template<class L> - void operator()(const L& lambda) { - lambda((const typename std::tuple_element<0, std::tuple<Args...>>::type*)nullptr); - } - }; - - template<size_t N> - struct iterator_impl<N> { - - template<class L> - void operator()(const std::tuple<>&, const L&, bool /*reverse*/ = true) { - //.. - } - - template<class L> - void operator()(const L&) { - //.. - } - }; - - template<class... Args> - struct iterator_impl2; - - template<> - struct iterator_impl2<> { - - template<class L> - void operator()(const L&) const { - //.. - } - }; - - template<class H, class... Tail> - struct iterator_impl2<H, Tail...> { - - template<class L> - void operator()(const L& lambda) const { - lambda((const H*)nullptr); - iterator_impl2<Tail...>{}(lambda); - } - }; - - template<class T> - struct iterator; - - template<class... Args> - struct iterator<std::tuple<Args...>> { - - template<class L> - void operator()(const L& lambda) const { - iterator_impl2<Args...>{}(lambda); - } - }; + [[noreturn]] inline void throw_translated_sqlite_error(int ev) { + throw sqlite_to_system_error(ev); } - namespace internal { - - // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer - template<class Function, class FunctionPointer, class Tuple, size_t... I> - auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence<I...>) { - return (f.*functionPointer)(std::get<I>(t)...); - } - - template<class Function, class FunctionPointer, class Tuple> - auto call(Function& f, FunctionPointer functionPointer, Tuple t) { - static constexpr auto size = std::tuple_size<Tuple>::value; - return call_impl(f, functionPointer, move(t), std::make_index_sequence<size>{}); - } - - template<class Function, class Tuple> - auto call(Function& f, Tuple t) { - return call(f, &Function::operator(), move(t)); - } - - template<size_t N, size_t I, class L, class R> - void move_tuple_impl(L& lhs, R& rhs) { - std::get<I>(lhs) = std::move(std::get<I>(rhs)); - internal::static_if<std::integral_constant<bool, N != I + 1>{}>([](auto& l, auto& r) { - move_tuple_impl<N, I + 1>(l, r); - })(lhs, rhs); - } - - template<size_t N, class L, class R> - void move_tuple(L& lhs, R& rhs) { - using bool_type = std::integral_constant<bool, N != 0>; - static_if<bool_type{}>([](auto& l, auto& r) { - move_tuple_impl<N, 0>(l, r); - })(lhs, rhs); - } - - template<class L, class... Args> - void iterate_tuple(const std::tuple<Args...>& tuple, const L& lambda) { - using tuple_type = std::tuple<Args...>; - tuple_helper::iterator_impl<std::tuple_size<tuple_type>::value - 1, Args...>()(tuple, lambda, false); - } - - template<class T, class L> - void iterate_tuple(const L& lambda) { - tuple_helper::iterator<T>{}(lambda); - } - - template<typename... input_t> - using tuple_cat_t = decltype(std::tuple_cat(std::declval<input_t>()...)); - - template<class... Args> - struct conc_tuple { - using type = tuple_cat_t<Args...>; - }; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3* db) { + throw sqlite_to_system_error(db); } -} -#pragma once - -#include <tuple> // std::tuple -#include <type_traits> // std::enable_if -namespace sqlite_orm { - namespace internal { - - template<class T, template<class> class C, class SFINAE = void> - struct find_in_tuple; - - template<template<class> class C> - struct find_in_tuple<std::tuple<>, C, void> { - using type = void; - }; - - template<class H, class... Args, template<class> class C> - struct find_in_tuple<std::tuple<H, Args...>, C, typename std::enable_if<C<H>::value>::type> { - using type = H; - }; - - template<class H, class... Args, template<class> class C> - struct find_in_tuple<std::tuple<H, Args...>, C, typename std::enable_if<!C<H>::value>::type> { - using type = typename find_in_tuple<std::tuple<Args...>, C>::type; - }; + [[noreturn]] inline void throw_translated_sqlite_error(sqlite3_stmt* stmt) { + throw sqlite_to_system_error(sqlite3_db_handle(stmt)); } } #pragma once -#include <tuple> // std::tuple - -namespace sqlite_orm { - namespace internal { +#include <string> // std::string +#include <memory> // std::shared_ptr, std::unique_ptr +#include <vector> // std::vector +// #include "functional/cxx_optional.h" - template<class T, template<class C> class F> - struct tuple_transformer; +// #include "cxx_core_features.h" - template<class... Args, template<class C> class F> - struct tuple_transformer<std::tuple<Args...>, F> { - using type = std::tuple<typename F<Args>::type...>; - }; - } -} -#pragma once +#if SQLITE_ORM_HAS_INCLUDE(<optional>) +#include <optional> +#endif -#include <tuple> // std::tuple +#if __cpp_lib_optional >= 201606L +#define SQLITE_ORM_OPTIONAL_SUPPORTED +#endif -namespace sqlite_orm { - namespace internal { +// #include "functional/cxx_type_traits_polyfill.h" - template<class T, template<class> class C> - struct count_tuple; +// #include "type_traits.h" - template<template<class> class C> - struct count_tuple<std::tuple<>, C> { - static constexpr const int value = 0; - }; +// #include "is_std_ptr.h" - template<class H, class... Args, template<class> class C> - struct count_tuple<std::tuple<H, Args...>, C> { - static constexpr const int value = C<H>::value + count_tuple<std::tuple<Args...>, C>::value; - }; - } -} -#pragma once +#include <type_traits> +#include <memory> namespace sqlite_orm { - namespace internal { - /** - * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise - */ - template<class... Args> - struct same_or_void { - using type = void; - }; - - template<class A> - struct same_or_void<A> { - using type = A; - }; + /** + * Specialization for optional type (std::shared_ptr / std::unique_ptr). + */ + template<typename T> + struct is_std_ptr : std::false_type {}; - template<class A, class B> - struct same_or_void<A, B> { - using type = void; - }; + template<typename T> + struct is_std_ptr<std::shared_ptr<T>> : std::true_type { + using element_type = typename std::shared_ptr<T>::element_type; - template<class A> - struct same_or_void<A, A> { - using type = A; - }; + static std::shared_ptr<T> make(std::remove_cv_t<T>&& v) { + return std::make_shared<T>(std::move(v)); + } + }; - template<class A, class... Args> - struct same_or_void<A, A, Args...> { - using type = typename same_or_void<A, Args...>::type; - }; + template<typename T> + struct is_std_ptr<std::unique_ptr<T>> : std::true_type { + using element_type = typename std::unique_ptr<T>::element_type; - } + static auto make(std::remove_cv_t<T>&& v) { + return std::make_unique<T>(std::move(v)); + } + }; } -#pragma once - -#include <string> // std::string -#include <memory> // std::shared_ptr, std::unique_ptr -#include <vector> // std::vector -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED namespace sqlite_orm { /** - * This class accepts c++ type and transfers it to sqlite name (int -> INTEGER, std::string -> TEXT) + * This class transforms a C++ type to a sqlite type name (int -> INTEGER, ...) */ template<class T, typename Enable = void> - struct type_printer; + struct type_printer {}; struct integer_printer { - inline const std::string& print() { + const std::string& print() const { static const std::string res = "INTEGER"; return res; } }; struct text_printer { - inline const std::string& print() { + const std::string& print() const { static const std::string res = "TEXT"; return res; } }; struct real_printer { - inline const std::string& print() { + const std::string& print() const { static const std::string res = "REAL"; return res; } }; struct blob_printer { - inline const std::string& print() { + const std::string& print() const { static const std::string res = "BLOB"; return res; } }; - // Note unsigned/signed char and simple char used for storing integer values, not char values. - template<> - struct type_printer<unsigned char, void> : public integer_printer {}; - - template<> - struct type_printer<signed char, void> : public integer_printer {}; - - template<> - struct type_printer<char, void> : public integer_printer {}; - - template<> - struct type_printer<unsigned short int, void> : public integer_printer {}; - - template<> - struct type_printer<short, void> : public integer_printer {}; - - template<> - struct type_printer<unsigned int, void> : public integer_printer {}; - - template<> - struct type_printer<int, void> : public integer_printer {}; - - template<> - struct type_printer<unsigned long, void> : public integer_printer {}; - - template<> - struct type_printer<long, void> : public integer_printer {}; - - template<> - struct type_printer<unsigned long long, void> : public integer_printer {}; - - template<> - struct type_printer<long long, void> : public integer_printer {}; - - template<> - struct type_printer<bool, void> : public integer_printer {}; - - template<> - struct type_printer<std::string, void> : public text_printer {}; - - template<> - struct type_printer<std::wstring, void> : public text_printer {}; - - template<> - struct type_printer<const char*, void> : public text_printer {}; - - template<> - struct type_printer<float, void> : public real_printer {}; + // Note: char, unsigned/signed char are used for storing integer values, not char values. + template<class T> + struct type_printer<T, + std::enable_if_t<polyfill::conjunction_v<polyfill::negation<internal::is_any_of<T, + wchar_t, +#ifdef __cpp_char8_t + char8_t, +#endif + char16_t, + char32_t>>, + std::is_integral<T>>>> : integer_printer { + }; - template<> - struct type_printer<double, void> : public real_printer {}; + template<class T> + struct type_printer<T, std::enable_if_t<std::is_floating_point<T>::value>> : real_printer {}; template<class T> - struct type_printer<std::shared_ptr<T>, void> : public type_printer<T> {}; + struct type_printer<T, + std::enable_if_t<polyfill::disjunction_v<std::is_same<T, const char*>, + std::is_base_of<std::string, T>, + std::is_base_of<std::wstring, T>>>> : text_printer {}; template<class T> - struct type_printer<std::unique_ptr<T>, void> : public type_printer<T> {}; + struct type_printer<T, std::enable_if_t<is_std_ptr<T>::value>> : type_printer<typename T::element_type> {}; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct type_printer<std::optional<T>, void> : public type_printer<T> {}; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + struct type_printer<T, std::enable_if_t<polyfill::is_specialization_of_v<T, std::optional>>> + : type_printer<typename T::value_type> {}; +#endif template<> - struct type_printer<std::vector<char>, void> : public blob_printer {}; + struct type_printer<std::vector<char>, void> : blob_printer {}; } #pragma once @@ -600,321 +637,574 @@ namespace sqlite_orm { } #pragma once +#include <system_error> // std::system_error +#include <ostream> // std::ostream #include <string> // std::string #include <tuple> // std::tuple, std::make_tuple -#include <sstream> // std::stringstream #include <type_traits> // std::is_base_of, std::false_type, std::true_type -#include <ostream> // std::ostream -// #include "table_type.h" +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/mpl.h" + +/* + * Symbols for 'template metaprogramming' (compile-time template programming), + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. + * + * Currently, the focus is on facilitating advanced type filtering, + * such as filtering columns by constraints having various traits. + * Hence it contains only a very small subset of a full MPL. + * + * Two key concepts are critical to understanding: + * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. + * 2. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn" + * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. + * 3. A 'metafunction operation' is an alias template that represents a function whose instantiation already yields a type. + * + * Conventions: + * - "Fn" is the name for a metafunction template template parameter. + * - "FnCls" is the name for a metafunction class template parameter. + * - "_fn" is a suffix for a type that accepts metafunctions and turns them into metafunction classes. + * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). + */ -#include <type_traits> // std::enable_if, std::is_member_pointer, std::is_member_function_pointer +#include <type_traits> // std::false_type, std::true_type -// #include "member_traits/getter_traits.h" +// #include "cxx_universal.h" -// #include "getters.h" +// #include "cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { + namespace mpl { + template<template<class...> class Fn> + struct indirectly_test_metafunction; - template<class O, class T> - using getter_by_value_const = T (O::*)() const; + /* + * Determines whether a class template has a nested metafunction `fn`. + * + * Implementation note: the technique of specialiazing on the inline variable must come first because + * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template<class T, class SFINAE = void> + SQLITE_ORM_INLINE_VAR constexpr bool is_metafunction_class_v = false; + template<class FnCls> + SQLITE_ORM_INLINE_VAR constexpr bool + is_metafunction_class_v<FnCls, polyfill::void_t<indirectly_test_metafunction<FnCls::template fn>>> = + true; - template<class O, class T> - using getter_by_value = T (O::*)(); + template<class T> + struct is_metafunction_class : polyfill::bool_constant<is_metafunction_class_v<T>> {}; - template<class O, class T> - using getter_by_ref_const = T& (O::*)() const; + /* + * Invoke metafunction. + */ + template<template<class...> class Fn, class... Args> + using invoke_fn_t = typename Fn<Args...>::type; - template<class O, class T> - using getter_by_ref = T& (O::*)(); +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template<template<class...> class Op, class... Args> + struct wrap_op { + using type = Op<Args...>; + }; - template<class O, class T> - using getter_by_const_ref_const = const T& (O::*)() const; + /* + * Invoke metafunction operation. + * + * Note: legacy compilers need an extra layer of indirection, otherwise type replacement may fail + * if alias template `Op` has a dependent expression in it. + */ + template<template<class...> class Op, class... Args> + using invoke_op_t = typename wrap_op<Op, Args...>::type; +#else + /* + * Invoke metafunction operation. + */ + template<template<class...> class Op, class... Args> + using invoke_op_t = Op<Args...>; +#endif - template<class O, class T> - using getter_by_const_ref = const T& (O::*)(); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template<class O, class T> - using getter_by_value_const_noexcept = T (O::*)() const noexcept; + /* + * Invoke metafunction class by invoking its nested metafunction. + */ + template<class FnCls, class... Args> + using invoke_t = typename FnCls::template fn<Args...>::type; - template<class O, class T> - using getter_by_value_noexcept = T (O::*)() noexcept; + /* + * Instantiate metafunction class' nested metafunction. + */ + template<class FnCls, class... Args> + using instantiate = typename FnCls::template fn<Args...>; - template<class O, class T> - using getter_by_ref_const_noexcept = T& (O::*)() const noexcept; + /* + * Wrap given type such that `typename T::type` is valid. + */ + template<class T, class SFINAE = void> + struct type_wrap : polyfill::type_identity<T> {}; + template<class T> + struct type_wrap<T, polyfill::void_t<typename T::type>> : T {}; - template<class O, class T> - using getter_by_ref_noexcept = T& (O::*)() noexcept; + /* + * Turn metafunction into a metafunction class. + * + * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). + * This is necessary because `fn` is a proxy to the originally quoted metafunction, + * and the instantiation of the metafunction might be an invalid expression. + */ + template<template<class...> class Fn> + struct quote_fn { + template<class InvocableTest, template<class...> class, class...> + struct invoke_fn; + + template<template<class...> class F, class... Args> + struct invoke_fn<polyfill::void_t<F<Args...>>, F, Args...> { + using type = type_wrap<F<Args...>>; + }; - template<class O, class T> - using getter_by_const_ref_const_noexcept = const T& (O::*)() const noexcept; + template<class... Args> + using fn = typename invoke_fn<void, Fn, Args...>::type; + }; - template<class O, class T> - using getter_by_const_ref_noexcept = const T& (O::*)() noexcept; -#endif - } -} + /* + * Indirection wrapper for higher-order metafunctions, + * specialized on the argument indexes where metafunctions appear. + */ + template<size_t...> + struct higherorder; + + template<> + struct higherorder<0u> { + /* + * Turn higher-order metafunction into a metafunction class. + */ + template<template<template<class...> class Fn, class... Args2> class HigherFn> + struct quote_fn { + template<class QuotedFn, class... Args2> + struct fn : HigherFn<QuotedFn::template fn, Args2...> {}; + }; + }; -namespace sqlite_orm { - namespace internal { + /* + * Metafunction class that extracts the nested metafunction of its metafunction class argument, + * quotes the extracted metafunction and passes it on to the next metafunction class + * (kind of the inverse of quoting). + */ + template<class FnCls> + struct pass_extracted_fn_to { + template<class... Args> + struct fn : FnCls::template fn<Args...> {}; + + // extract, quote, pass on + template<template<class...> class Fn, class... Args> + struct fn<Fn<Args...>> : FnCls::template fn<quote_fn<Fn>> {}; + }; - template<class T> - struct getter_traits; + /* + * Metafunction class that invokes the specified metafunction operation, + * and passes its result on to the next metafunction class. + */ + template<template<class...> class Op, class FnCls> + struct pass_result_to { + // call Op, pass on its result + template<class... Args> + struct fn : FnCls::template fn<Op<Args...>> {}; + }; - template<class O, class T> - struct getter_traits<getter_by_value_const<O, T>> { - using object_type = O; - using field_type = T; + /* + * Bind arguments at the front of a metafunction class. + * Metafunction class equivalent to std::bind_front(). + */ + template<class FnCls, class... Bound> + struct bind_front { + template<class... Args> + struct fn : FnCls::template fn<Bound..., Args...> {}; + }; - static constexpr const bool returns_lvalue = false; - }; + /* + * Bind arguments at the back of a metafunction class. + * Metafunction class equivalent to std::bind_back() + */ + template<class FnCls, class... Bound> + struct bind_back { + template<class... Args> + struct fn : FnCls::template fn<Args..., Bound...> {}; + }; - template<class O, class T> - struct getter_traits<getter_by_value<O, T>> { - using object_type = O; - using field_type = T; + /* + * Metafunction class equivalent to polyfill::always_false. + * It ignores arguments passed to the metafunction, + * and always returns the given type. + */ + template<class T> + struct always { + template<class...> + struct fn : type_wrap<T> {}; + }; - static constexpr const bool returns_lvalue = false; - }; + /* + * Unary metafunction class equivalent to std::type_identity. + */ + struct identity { + template<class T> + struct fn : type_wrap<T> {}; + }; - template<class O, class T> - struct getter_traits<getter_by_ref_const<O, T>> { - using object_type = O; - using field_type = T; + /* + * Metafunction class equivalent to std::negation. + */ + template<class FnCls> + struct not_ { + template<class... Args> + struct fn : polyfill::negation<invoke_t<FnCls, Args...>> {}; + }; - static constexpr const bool returns_lvalue = true; - }; + /* + * Metafunction class equivalent to std::conjunction + */ + template<class... TraitFnCls> + struct conjunction { + template<class... Args> + struct fn : polyfill::conjunction<typename TraitFnCls::template fn<Args...>...> {}; + }; - template<class O, class T> - struct getter_traits<getter_by_ref<O, T>> { - using object_type = O; - using field_type = T; + /* + * Metafunction class equivalent to std::disjunction. + */ + template<class... TraitFnCls> + struct disjunction { + template<class... Args> + struct fn : polyfill::disjunction<typename TraitFnCls::template fn<Args...>...> {}; + }; - static constexpr const bool returns_lvalue = true; - }; +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + /* + * Metafunction equivalent to std::conjunction. + */ + template<template<class...> class... TraitFn> + using conjunction_fn = conjunction<quote_fn<TraitFn>...>; - template<class O, class T> - struct getter_traits<getter_by_const_ref_const<O, T>> { - using object_type = O; - using field_type = T; + /* + * Metafunction equivalent to std::disjunction. + */ + template<template<class...> class... TraitFn> + using disjunction_fn = disjunction<quote_fn<TraitFn>...>; +#else + template<template<class...> class... TraitFn> + struct conjunction_fn : conjunction<quote_fn<TraitFn>...> {}; - static constexpr const bool returns_lvalue = true; - }; + template<template<class...> class... TraitFn> + struct disjunction_fn : disjunction<quote_fn<TraitFn>...> {}; +#endif - template<class O, class T> - struct getter_traits<getter_by_const_ref<O, T>> { - using object_type = O; - using field_type = T; + /* + * Convenience template alias for binding arguments at the front of a metafunction. + */ + template<template<class...> class Fn, class... Bound> + using bind_front_fn = bind_front<quote_fn<Fn>, Bound...>; - static constexpr const bool returns_lvalue = true; - }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template<class O, class T> - struct getter_traits<getter_by_value_const_noexcept<O, T>> { - using object_type = O; - using field_type = T; + /* + * Convenience template alias for binding arguments at the back of a metafunction. + */ + template<template<class...> class Fn, class... Bound> + using bind_back_fn = bind_back<quote_fn<Fn>, Bound...>; - static constexpr const bool returns_lvalue = false; - }; + /* + * Convenience template alias for binding a metafunction at the front of a higher-order metafunction. + */ + template<template<template<class...> class Fn, class... Args2> class HigherFn, + template<class...> + class BoundFn, + class... Bound> + using bind_front_higherorder_fn = + bind_front<higherorder<0>::quote_fn<HigherFn>, quote_fn<BoundFn>, Bound...>; + } + } - template<class O, class T> - struct getter_traits<getter_by_value_noexcept<O, T>> { - using object_type = O; - using field_type = T; + namespace mpl = internal::mpl; - static constexpr const bool returns_lvalue = false; - }; + // convenience metafunction classes + namespace internal { + /* + * Trait metafunction class that checks if a type has the specified trait. + */ + template<template<class...> class TraitFn> + using check_if = mpl::quote_fn<TraitFn>; - template<class O, class T> - struct getter_traits<getter_by_ref_const_noexcept<O, T>> { - using object_type = O; - using field_type = T; + /* + * Trait metafunction class that checks if a type doesn't have the specified trait. + */ + template<template<class...> class TraitFn> + using check_if_not = mpl::not_<mpl::quote_fn<TraitFn>>; - static constexpr const bool returns_lvalue = true; - }; + /* + * Trait metafunction class that checks if a type is the same as the specified type. + */ + template<class Type> + using check_if_is_type = mpl::bind_front_fn<std::is_same, Type>; - template<class O, class T> - struct getter_traits<getter_by_ref_noexcept<O, T>> { - using object_type = O; - using field_type = T; + /* + * Trait metafunction class that checks if a type's template matches the specified template + * (similar to `is_specialization_of`). + */ + template<template<class...> class Template> + using check_if_is_template = + mpl::pass_extracted_fn_to<mpl::bind_front_fn<std::is_same, mpl::quote_fn<Template>>>; + } +} - static constexpr const bool returns_lvalue = true; - }; +// #include "tuple_helper/same_or_void.h" - template<class O, class T> - struct getter_traits<getter_by_const_ref_const_noexcept<O, T>> { - using object_type = O; - using field_type = T; +namespace sqlite_orm { + namespace internal { - static constexpr const bool returns_lvalue = true; + /** + * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise + */ + template<class... Args> + struct same_or_void { + using type = void; }; - template<class O, class T> - struct getter_traits<getter_by_const_ref_noexcept<O, T>> { - using object_type = O; - using field_type = T; + template<class A> + struct same_or_void<A> { + using type = A; + }; - static constexpr const bool returns_lvalue = true; + template<class A> + struct same_or_void<A, A> { + using type = A; }; -#endif + + template<class A, class... Args> + struct same_or_void<A, A, Args...> : same_or_void<A, Args...> {}; + } } -// #include "member_traits/setter_traits.h" +// #include "tuple_helper/tuple_traits.h" -// #include "setters.h" +#include <type_traits> // std::is_same +#include <tuple> + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/mpl.h" namespace sqlite_orm { namespace internal { + /* + * Higher-order trait metafunction that checks whether a tuple contains a type with given trait. + */ + template<template<class...> class TraitFn, class Tuple> + struct tuple_has {}; + template<template<class...> class TraitFn, class... Types> + struct tuple_has<TraitFn, std::tuple<Types...>> : polyfill::disjunction<TraitFn<Types>...> {}; - template<class O, class T> - using setter_by_value = void (O::*)(T); - - template<class O, class T> - using setter_by_ref = void (O::*)(T&); + /* + * Trait metafunction class that checks whether a tuple contains a type with given trait. + */ + template<template<class...> class TraitFn> + using check_if_tuple_has = mpl::bind_front_higherorder_fn<tuple_has, TraitFn>; - template<class O, class T> - using setter_by_const_ref = void (O::*)(const T&); -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template<class O, class T> - using setter_by_value_noexcept = void (O::*)(T) noexcept; + /* + * Trait metafunction class that checks whether a tuple doesn't contain a type with given trait. + */ + template<template<class...> class TraitFn> + using check_if_tuple_has_not = mpl::not_<check_if_tuple_has<TraitFn>>; - template<class O, class T> - using setter_by_ref_noexcept = void (O::*)(T&) noexcept; + /* + * Metafunction class that checks whether a tuple contains given type. + */ + template<class T> + using check_if_tuple_has_type = mpl::bind_front_higherorder_fn<tuple_has, check_if_is_type<T>::template fn>; - template<class O, class T> - using setter_by_const_ref_noexcept = void (O::*)(const T&) noexcept; -#endif + /* + * Metafunction class that checks whether a tuple contains a given template. + * + * Note: we are using 2 small tricks: + * 1. A template template parameter can be treated like a metafunction, so we can just "quote" a 'primary' + * template into the MPL system (e.g. `std::vector`). + * 2. This metafunction class does the opposite of the trait function `is_specialization`: + * `is_specialization` tries to instantiate the primary template template parameter using the + * template parameters of a template type, then compares both instantiated types. + * Here instead, `pass_extracted_fn_to` extracts the template template parameter from a template type, + * then compares the resulting template template parameters. + */ + template<template<class...> class Primary> + using check_if_tuple_has_template = + mpl::bind_front_higherorder_fn<tuple_has, check_if_is_template<Primary>::template fn>; } } +// #include "tuple_helper/tuple_filter.h" + +#include <type_traits> // std::integral_constant, std::index_sequence, std::conditional, std::declval +#include <tuple> // std::tuple + +// #include "../functional/cxx_universal.h" + +// #include "../functional/index_sequence_util.h" + +#include <utility> // std::index_sequence, std::make_index_sequence + +// #include "../functional/cxx_universal.h" + +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED +#include <array> +#endif namespace sqlite_orm { namespace internal { + /** + * Get the first value of an index_sequence. + */ + template<size_t I, size_t... Idx> + SQLITE_ORM_CONSTEVAL size_t first_index_sequence_value(std::index_sequence<I, Idx...>) { + return I; + } - template<class T> - struct setter_traits; +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + /** + * Reorder the values of an index_sequence according to the positions from a second sequence. + */ + template<size_t... Value, size_t... IdxOfValue> + SQLITE_ORM_CONSTEVAL auto reorder_index_sequence(std::index_sequence<Value...>, + std::index_sequence<IdxOfValue...>) { + constexpr std::array<size_t, sizeof...(Value)> values{Value...}; + return std::index_sequence<values[sizeof...(Value) - 1u - IdxOfValue]...>{}; + } - template<class O, class T> - struct setter_traits<setter_by_value<O, T>> { - using object_type = O; - using field_type = T; - }; + template<size_t Value, size_t IdxOfValue> + SQLITE_ORM_CONSTEVAL std::index_sequence<Value> reorder_index_sequence(std::index_sequence<Value>, + std::index_sequence<IdxOfValue>) { + return {}; + } - template<class O, class T> - struct setter_traits<setter_by_ref<O, T>> { - using object_type = O; - using field_type = T; - }; + inline SQLITE_ORM_CONSTEVAL std::index_sequence<> reorder_index_sequence(std::index_sequence<>, + std::index_sequence<>) { + return {}; + } - template<class O, class T> - struct setter_traits<setter_by_const_ref<O, T>> { - using object_type = O; - using field_type = T; - }; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template<class O, class T> - struct setter_traits<setter_by_value_noexcept<O, T>> { - using object_type = O; - using field_type = T; - }; + /** + * Reverse the values of an index_sequence. + */ + template<size_t... Idx> + SQLITE_ORM_CONSTEVAL auto reverse_index_sequence(std::index_sequence<Idx...>) { + return reorder_index_sequence(std::index_sequence<Idx...>{}, std::make_index_sequence<sizeof...(Idx)>{}); + } +#endif - template<class O, class T> - struct setter_traits<setter_by_ref_noexcept<O, T>> { - using object_type = O; - using field_type = T; + template<class... Seq> + struct flatten_idxseq { + using type = std::index_sequence<>; }; - template<class O, class T> - struct setter_traits<setter_by_const_ref_noexcept<O, T>> { - using object_type = O; - using field_type = T; + template<size_t... Ix> + struct flatten_idxseq<std::index_sequence<Ix...>> { + using type = std::index_sequence<Ix...>; }; -#endif - } -} -// #include "member_traits/is_getter.h" + template<size_t... As, size_t... Bs, class... Seq> + struct flatten_idxseq<std::index_sequence<As...>, std::index_sequence<Bs...>, Seq...> + : flatten_idxseq<std::index_sequence<As..., Bs...>, Seq...> {}; -#include <type_traits> // std::false_type, std::true_type - -// #include "getters.h" + template<class... Seq> + using flatten_idxseq_t = typename flatten_idxseq<Seq...>::type; + } +} namespace sqlite_orm { namespace internal { - template<class T> - struct is_getter : std::false_type {}; - - template<class O, class T> - struct is_getter<getter_by_value_const<O, T>> : std::true_type {}; + template<typename... input_t> + using tuple_cat_t = decltype(std::tuple_cat(std::declval<input_t>()...)); - template<class O, class T> - struct is_getter<getter_by_value<O, T>> : std::true_type {}; + template<class... Tpl> + struct conc_tuple { + using type = tuple_cat_t<Tpl...>; + }; - template<class O, class T> - struct is_getter<getter_by_ref_const<O, T>> : std::true_type {}; + template<class Tpl, class Seq> + struct tuple_from_index_sequence; - template<class O, class T> - struct is_getter<getter_by_ref<O, T>> : std::true_type {}; + template<class Tpl, size_t... Idx> + struct tuple_from_index_sequence<Tpl, std::index_sequence<Idx...>> { + using type = std::tuple<std::tuple_element_t<Idx, Tpl>...>; + }; - template<class O, class T> - struct is_getter<getter_by_const_ref_const<O, T>> : std::true_type {}; + template<class Tpl, class Seq> + using tuple_from_index_sequence_t = typename tuple_from_index_sequence<Tpl, Seq>::type; - template<class O, class T> - struct is_getter<getter_by_const_ref<O, T>> : std::true_type {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template<class O, class T> - struct is_getter<getter_by_value_const_noexcept<O, T>> : std::true_type {}; + template<class Tpl, template<class...> class Pred, template<class...> class Proj, class Seq> + struct filter_tuple_sequence; - template<class O, class T> - struct is_getter<getter_by_value_noexcept<O, T>> : std::true_type {}; +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template<class Tpl, template<class...> class Pred, template<class...> class Proj, size_t... Idx> + struct filter_tuple_sequence<Tpl, Pred, Proj, std::index_sequence<Idx...>> + : flatten_idxseq<std::conditional_t<Pred<Proj<std::tuple_element_t<Idx, Tpl>>>::value, + std::index_sequence<Idx>, + std::index_sequence<>>...> {}; +#else + template<size_t Idx, class T, template<class...> class Pred, class SFINAE = void> + struct tuple_seq_single { + using type = std::index_sequence<>; + }; - template<class O, class T> - struct is_getter<getter_by_ref_const_noexcept<O, T>> : std::true_type {}; + template<size_t Idx, class T, template<class...> class Pred> + struct tuple_seq_single<Idx, T, Pred, std::enable_if_t<Pred<T>::value>> { + using type = std::index_sequence<Idx>; + }; - template<class O, class T> - struct is_getter<getter_by_ref_noexcept<O, T>> : std::true_type {}; + template<class Tpl, template<class...> class Pred, template<class...> class Proj, size_t... Idx> + struct filter_tuple_sequence<Tpl, Pred, Proj, std::index_sequence<Idx...>> + : flatten_idxseq<typename tuple_seq_single<Idx, Proj<std::tuple_element_t<Idx, Tpl>>, Pred>::type...> {}; +#endif - template<class O, class T> - struct is_getter<getter_by_const_ref_const_noexcept<O, T>> : std::true_type {}; + template<class Tpl, + template<class...> + class Pred, + template<class...> class Proj = polyfill::type_identity_t, + class Seq = std::make_index_sequence<std::tuple_size<Tpl>::value>> + using filter_tuple_sequence_t = typename filter_tuple_sequence<Tpl, Pred, Proj, Seq>::type; + + template<class Tpl, + template<class...> + class Pred, + template<class...> class FilterProj = polyfill::type_identity_t, + class Seq = std::make_index_sequence<std::tuple_size<Tpl>::value>> + using filter_tuple_t = tuple_from_index_sequence_t<Tpl, filter_tuple_sequence_t<Tpl, Pred, FilterProj, Seq>>; + + template<class Tpl, + template<class...> + class Pred, + template<class...> class FilterProj = polyfill::type_identity_t> + struct count_tuple : std::integral_constant<int, filter_tuple_sequence_t<Tpl, Pred, FilterProj>::size()> {}; - template<class O, class T> - struct is_getter<getter_by_const_ref_noexcept<O, T>> : std::true_type {}; -#endif + /* + * Count a tuple, picking only those elements specified in the index sequence. + * + * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems + * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. + */ + template<class Tpl, + template<class...> + class Pred, + class Seq, + template<class...> class FilterProj = polyfill::type_identity_t> + struct count_filtered_tuple + : std::integral_constant<size_t, filter_tuple_sequence_t<Tpl, Pred, FilterProj, Seq>::size()> {}; } } -// #include "member_traits/is_setter.h" - -// #include "setters.h" - -namespace sqlite_orm { - namespace internal { +// #include "type_traits.h" - template<class T> - struct is_setter : std::false_type {}; - - template<class O, class T> - struct is_setter<setter_by_value<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_setter<setter_by_ref<O, T>> : std::true_type {}; - - template<class O, class T> - struct is_setter<setter_by_const_ref<O, T>> : std::true_type {}; -#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED - template<class O, class T> - struct is_setter<setter_by_value_noexcept<O, T>> : std::true_type {}; +// #include "collate_argument.h" - template<class O, class T> - struct is_setter<setter_by_ref_noexcept<O, T>> : std::true_type {}; +// #include "error_code.h" - template<class O, class T> - struct is_setter<setter_by_const_ref_noexcept<O, T>> : std::true_type {}; -#endif - } -} +// #include "table_type_of.h" namespace sqlite_orm { @@ -928,38 +1218,29 @@ namespace sqlite_orm { * T - member pointer * `type` is a type which is mapped. * E.g. - * - `table_type<decltype(&User::id)>::type` is `User` - * - `table_type<decltype(&User::getName)>::type` is `User` - * - `table_type<decltype(&User::setName)>::type` is `User` + * - `table_type_of<decltype(&User::id)>::type` is `User` + * - `table_type_of<decltype(&User::getName)>::type` is `User` + * - `table_type_of<decltype(&User::setName)>::type` is `User` */ - template<class T, class SFINAE = void> - struct table_type; + template<class T> + struct table_type_of; template<class O, class F> - struct table_type<F O::*, - typename std::enable_if<std::is_member_pointer<F O::*>::value && - !std::is_member_function_pointer<F O::*>::value>::type> { + struct table_type_of<F O::*> { using type = O; }; - template<class T> - struct table_type<T, typename std::enable_if<is_getter<T>::value>::type> { - using type = typename getter_traits<T>::object_type; - }; - - template<class T> - struct table_type<T, typename std::enable_if<is_setter<T>::value>::type> { - using type = typename setter_traits<T>::object_type; - }; - template<class T, class F> - struct table_type<column_pointer<T, F>, void> { + struct table_type_of<column_pointer<T, F>> { using type = T; }; + + template<class T> + using table_type_of_t = typename table_type_of<T>::type; } } -// #include "tuple_helper/tuple_helper.h" +// #include "type_printer.h" namespace sqlite_orm { @@ -968,11 +1249,14 @@ namespace sqlite_orm { /** * AUTOINCREMENT constraint class. */ - struct autoincrement_t { + struct autoincrement_t {}; - operator std::string() const { - return "AUTOINCREMENT"; - } + enum class conflict_clause_t { + rollback, + abort, + fail, + ignore, + replace, }; struct primary_key_base { @@ -981,48 +1265,85 @@ namespace sqlite_orm { ascending, descending, }; + struct { + order_by asc_option = order_by::unspecified; + conflict_clause_t conflict_clause = conflict_clause_t::rollback; + bool conflict_clause_is_on = false; + } options; + }; - order_by asc_option = order_by::unspecified; + template<class T> + struct primary_key_with_autoincrement { + using primary_key_type = T; - operator std::string() const { - std::string res = "PRIMARY KEY"; - switch(this->asc_option) { - case order_by::ascending: - res += " ASC"; - break; - case order_by::descending: - res += " DESC"; - break; - default: - break; - } - return res; - } + primary_key_type primary_key; + + primary_key_with_autoincrement(primary_key_type primary_key_) : primary_key(primary_key_) {} }; /** * PRIMARY KEY constraint class. * Cs is parameter pack which contains columns (member pointers and/or function pointers). Can be empty when - * used withen `make_column` function. + * used within `make_column` function. */ template<class... Cs> struct primary_key_t : primary_key_base { + using self = primary_key_t<Cs...>; using order_by = primary_key_base::order_by; using columns_tuple = std::tuple<Cs...>; columns_tuple columns; - primary_key_t(decltype(columns) c) : columns(move(c)) {} + primary_key_t(decltype(columns) columns) : columns(move(columns)) {} + + self asc() const { + auto res = *this; + res.options.asc_option = order_by::ascending; + return res; + } + + self desc() const { + auto res = *this; + res.options.asc_option = order_by::descending; + return res; + } + + primary_key_with_autoincrement<self> autoincrement() const { + return {*this}; + } + + self on_conflict_rollback() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::rollback; + return res; + } + + self on_conflict_abort() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::abort; + return res; + } + + self on_conflict_fail() const { + auto res = *this; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::fail; + return res; + } - primary_key_t<Cs...> asc() const { + self on_conflict_ignore() const { auto res = *this; - res.asc_option = order_by::ascending; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::ignore; return res; } - primary_key_t<Cs...> desc() const { + self on_conflict_replace() const { auto res = *this; - res.asc_option = order_by::descending; + res.options.conflict_clause_is_on = true; + res.options.conflict_clause = conflict_clause_t::replace; return res; } }; @@ -1083,22 +1404,22 @@ namespace sqlite_orm { inline std::ostream& operator<<(std::ostream& os, foreign_key_action action) { switch(action) { - case decltype(action)::no_action: + case foreign_key_action::no_action: os << "NO ACTION"; break; - case decltype(action)::restrict_: + case foreign_key_action::restrict_: os << "RESTRICT"; break; - case decltype(action)::set_null: + case foreign_key_action::set_null: os << "SET NULL"; break; - case decltype(action)::set_default: + case foreign_key_action::set_default: os << "SET DEFAULT"; break; - case decltype(action)::cascade: + case foreign_key_action::cascade: os << "CASCADE"; break; - case decltype(action)::none: + case foreign_key_action::none: break; } return os; @@ -1181,7 +1502,7 @@ namespace sqlite_orm { } operator bool() const { - return this->_action != decltype(this->_action)::none; + return this->_action != foreign_key_action::none; } }; @@ -1199,12 +1520,12 @@ namespace sqlite_orm { /** * Holds obect type of all referenced columns. */ - using target_type = typename same_or_void<typename table_type<Rs>::type...>::type; + using target_type = typename same_or_void<table_type_of_t<Rs>...>::type; /** * Holds obect type of all source columns. */ - using source_type = typename same_or_void<typename table_type<Cs>::type...>::type; + using source_type = typename same_or_void<table_type_of_t<Cs>...>::type; columns_type columns; references_type references; @@ -1231,14 +1552,6 @@ namespace sqlite_orm { this->on_delete = {*this, false, other.on_delete._action}; return *this; } - - template<class L> - void for_each_column(const L&) {} - - template<class... Opts> - constexpr bool has_every() const { - return false; - } }; template<class A, class B> @@ -1258,8 +1571,6 @@ namespace sqlite_orm { tuple_type columns; - foreign_key_intermediate_t(tuple_type columns_) : columns(std::move(columns_)) {} - template<class... Rs> foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>> references(Rs... refs) { return {std::move(this->columns), std::make_tuple(std::forward<Rs>(refs)...)}; @@ -1268,81 +1579,146 @@ namespace sqlite_orm { #endif struct collate_constraint_t { - internal::collate_argument argument = internal::collate_argument::binary; - - collate_constraint_t(internal::collate_argument argument_) : argument(argument_) {} + collate_argument argument = collate_argument::binary; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + collate_constraint_t(collate_argument argument) : argument{argument} {} +#endif operator std::string() const { - std::string res = "COLLATE " + this->string_from_collate_argument(this->argument); - return res; + return "COLLATE " + this->string_from_collate_argument(this->argument); } - static std::string string_from_collate_argument(internal::collate_argument argument) { + static std::string string_from_collate_argument(collate_argument argument) { switch(argument) { - case decltype(argument)::binary: + case collate_argument::binary: return "BINARY"; - case decltype(argument)::nocase: + case collate_argument::nocase: return "NOCASE"; - case decltype(argument)::rtrim: + case collate_argument::rtrim: return "RTRIM"; } - throw std::system_error(std::make_error_code(orm_error_code::invalid_collate_argument_enum)); + throw std::system_error{orm_error_code::invalid_collate_argument_enum}; } }; - struct check_string { - operator std::string() const { - return "CHECK"; - } + template<class T> + struct check_t { + using expression_type = T; + + expression_type expression; + }; + + struct basic_generated_always { + enum class storage_type { + not_specified, + virtual_, + stored, + }; + + bool full = true; + storage_type storage = storage_type::not_specified; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {} +#endif }; template<class T> - struct check_t : check_string { + struct generated_always_t : basic_generated_always { using expression_type = T; expression_type expression; - check_t(expression_type expression_) : expression(std::move(expression_)) {} + generated_always_t(expression_type expression_, bool full, storage_type storage) : + basic_generated_always{full, storage}, expression(std::move(expression_)) {} + + generated_always_t<T> virtual_() { + return {std::move(this->expression), this->full, storage_type::virtual_}; + } + + generated_always_t<T> stored() { + return {std::move(this->expression), this->full, storage_type::stored}; + } }; + } + + namespace internal { + template<class T> - struct is_constraint : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = polyfill::is_specialization_of_v<T, foreign_key_t>; - template<> - struct is_constraint<autoincrement_t> : std::true_type {}; + template<class T> + using is_foreign_key = polyfill::bool_constant<is_foreign_key_v<T>>; + + template<class T> + struct is_primary_key : std::false_type {}; template<class... Cs> - struct is_constraint<primary_key_t<Cs...>> : std::true_type {}; + struct is_primary_key<primary_key_t<Cs...>> : std::true_type {}; - template<class... Args> - struct is_constraint<unique_t<Args...>> : std::true_type {}; + template<class T> + struct is_primary_key<primary_key_with_autoincrement<T>> : std::true_type {}; template<class T> - struct is_constraint<default_t<T>> : std::true_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = is_primary_key<T>::value; - template<class C, class R> - struct is_constraint<foreign_key_t<C, R>> : std::true_type {}; + template<class T> + using is_generated_always = polyfill::is_specialization_of<T, generated_always_t>; - template<> - struct is_constraint<collate_constraint_t> : std::true_type {}; + template<class T> + SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = is_generated_always<T>::value; template<class T> - struct is_constraint<check_t<T>> : std::true_type {}; + using is_autoincrement = std::is_same<T, autoincrement_t>; - template<class... Args> - struct constraints_size; + template<class T> + SQLITE_ORM_INLINE_VAR constexpr bool is_autoincrement_v = is_autoincrement<T>::value; - template<> - struct constraints_size<> { - static constexpr const int value = 0; - }; + /** + * PRIMARY KEY INSERTABLE traits. + */ + template<typename T> + struct is_primary_key_insertable + : polyfill::disjunction< + mpl::instantiate<mpl::disjunction<check_if_tuple_has<is_autoincrement>, + check_if_tuple_has_template<default_t>, + check_if_tuple_has_template<primary_key_with_autoincrement>>, + constraints_type_t<T>>, + std::is_base_of<integer_printer, type_printer<field_type_t<T>>>> { - template<class H, class... Args> - struct constraints_size<H, Args...> { - static constexpr const int value = is_constraint<H>::value + constraints_size<Args...>::value; + static_assert(tuple_has<is_primary_key, constraints_type_t<T>>::value, "an unexpected type was passed"); }; + + template<class T> + using is_constraint = + mpl::instantiate<mpl::disjunction<check_if<is_autoincrement>, + check_if<is_primary_key>, + check_if<is_foreign_key>, + check_if_is_template<unique_t>, + check_if_is_template<default_t>, + check_if_is_template<check_t>, + check_if_is_template<primary_key_with_autoincrement>, + check_if_is_type<collate_constraint_t>, +#if SQLITE_VERSION_NUMBER >= 3031000 + check_if<is_generated_always>, +#endif + // dummy tail because of SQLITE_VERSION_NUMBER checks above + mpl::always<std::false_type>>, + T>; } +#if SQLITE_VERSION_NUMBER >= 3031000 + template<class T> + internal::generated_always_t<T> generated_always_as(T expression) { + return {std::move(expression), true, internal::basic_generated_always::storage_type::not_specified}; + } + + template<class T> + internal::generated_always_t<T> as(T expression) { + return {std::move(expression), false, internal::basic_generated_always::storage_type::not_specified}; + } +#endif #if SQLITE_VERSION_NUMBER >= 3006019 /** @@ -1367,7 +1743,11 @@ namespace sqlite_orm { return {{}}; } - inline internal::autoincrement_t autoincrement() { + /** + * AUTOINCREMENT keyword. [Deprecation notice] Use `primary_key().autoincrement()` instead of using this function. + * This function will be removed in 1.9 + */ + [[deprecated("Use primary_key().autoincrement()` instead")]] inline internal::autoincrement_t autoincrement() { return {}; } @@ -1401,59 +1781,14 @@ namespace sqlite_orm { internal::check_t<T> check(T t) { return {std::move(t)}; } - - namespace internal { - - /** - * FOREIGN KEY traits. Common case - */ - template<class T> - struct is_foreign_key : std::false_type {}; - - /** - * FOREIGN KEY traits. Specialized case - */ - template<class C, class R> - struct is_foreign_key<internal::foreign_key_t<C, R>> : std::true_type {}; - - /** - * PRIMARY KEY traits. Common case - */ - template<class T> - struct is_primary_key : public std::false_type {}; - - /** - * PRIMARY KEY traits. Specialized case - */ - template<class... Cs> - struct is_primary_key<internal::primary_key_t<Cs...>> : public std::true_type {}; - - /** - * PRIMARY KEY INSERTABLE traits. - */ - template<typename T> - struct is_primary_key_insertable { - using field_type = typename T::field_type; - using constraints_type = typename T::constraints_type; - - static_assert((tuple_helper::tuple_contains_type<primary_key_t<>, constraints_type>::value), - "an unexpected type was passed"); - - static constexpr bool value = - (tuple_helper::tuple_contains_some_type<default_t, constraints_type>::value || - tuple_helper::tuple_contains_type<autoincrement_t, constraints_type>::value || - std::is_base_of<integer_printer, type_printer<field_type>>::value); - }; - } - } #pragma once -#include <type_traits> // std::false_type, std::true_type +#include <type_traits> // std::false_type, std::true_type, std::enable_if #include <memory> // std::shared_ptr, std::unique_ptr -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -1464,144 +1799,74 @@ namespace sqlite_orm { * custom type as `NULL` (for example: boost::optional) you have to create a specialiation * of type_is_nullable for your type and derive from `std::true_type`. */ - template<class T> - struct type_is_nullable : public std::false_type { + template<class T, class SFINAE = void> + struct type_is_nullable : std::false_type { bool operator()(const T&) const { return true; } }; /** - * This is a specialization for std::shared_ptr. std::shared_ptr is nullable in sqlite_orm. - */ - template<class T> - struct type_is_nullable<std::shared_ptr<T>> : public std::true_type { - bool operator()(const std::shared_ptr<T>& t) const { - return static_cast<bool>(t); - } - }; - - /** - * This is a specialization for std::unique_ptr. std::unique_ptr is nullable too. + * This is a specialization for std::shared_ptr, std::unique_ptr, std::optional, which are nullable in sqlite_orm. */ template<class T> - struct type_is_nullable<std::unique_ptr<T>> : public std::true_type { - bool operator()(const std::unique_ptr<T>& t) const { - return static_cast<bool>(t); - } - }; - + struct type_is_nullable<T, + std::enable_if_t<polyfill::disjunction_v< #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - /** - * This is a specialization for std::optional. std::optional is nullable. - */ - template<class T> - struct type_is_nullable<std::optional<T>> : public std::true_type { - bool operator()(const std::optional<T>& t) const { - return t.has_value(); + polyfill::is_specialization_of<T, std::optional>, +#endif + polyfill::is_specialization_of<T, std::unique_ptr>, + polyfill::is_specialization_of<T, std::shared_ptr>>>> : std::true_type { + bool operator()(const T& t) const { + return static_cast<bool>(t); } }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED } #pragma once -#include <memory> // std::unique_ptr -#include <string> // std::string -#include <sstream> // std::stringstream - -// #include "constraints.h" +#include <type_traits> // std::false_type, std::true_type +#include <utility> // std::move +// #include "functional/cxx_optional.h" -// #include "serializator_context.h" +// #include "tags.h" namespace sqlite_orm { - namespace internal { + struct negatable_t {}; - struct serializator_context_base { - bool replace_bindable_with_question = false; - bool skip_table_name = true; - bool use_parentheses = true; - - template<class O, class F> - const std::string* column_name(F O::*) const { - return nullptr; - } - }; - - template<class I> - struct serializator_context : serializator_context_base { - using impl_type = I; - - const impl_type& impl; - - serializator_context(const impl_type& impl_) : impl(impl_) {} - - template<class O, class F> - const std::string* column_name(F O::*m) const { - return this->impl.column_name(m); - } - }; - - template<class S> - struct serializator_context_builder { - using storage_type = S; - using impl_type = typename storage_type::impl_type; - - serializator_context_builder(const storage_type& storage_) : storage(storage_) {} - - serializator_context<impl_type> operator()() const { - return {this->storage.impl}; - } - - const storage_type& storage; - }; - + /** + * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators + */ + struct condition_t {}; } - } -namespace sqlite_orm { - - namespace internal { - - /** - * This class is used in tuple interation to know whether tuple constains `default_value_t` - * constraint class and what it's value if it is - */ - struct default_value_extractor { +// #include "serialize_result_type.h" - template<class A> - std::unique_ptr<std::string> operator()(const A&) { - return {}; - } +// #include "functional/cxx_string_view.h" - template<class T> - std::unique_ptr<std::string> operator()(const default_t<T>& t) { - serializator_context_base context; - return std::make_unique<std::string>(serialize(t.value, context)); - } - }; +// #include "cxx_core_features.h" - } +#if SQLITE_ORM_HAS_INCLUDE(<string_view>) +#include <string_view> +#endif -} -#pragma once +#if __cpp_lib_string_view >= 201606L +#define SQLITE_ORM_STRING_VIEW_SUPPORTED +#endif -#include <type_traits> // std::false_type, std::true_type -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::nullopt -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED -// #include "tags.h" +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include <string> // std::string +#endif namespace sqlite_orm { namespace internal { - struct negatable_t {}; - - /** - * Inherit from this class if target class can be chained with other conditions with '&&' and '||' operators - */ - struct condition_t {}; +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + using serialize_result_type = std::string_view; +#else + using serialize_result_type = std::string; +#endif } } @@ -1626,7 +1891,7 @@ namespace sqlite_orm { }; struct conc_string { - operator std::string() const { + serialize_result_type serialize() const { return "||"; } }; @@ -1638,7 +1903,7 @@ namespace sqlite_orm { using conc_t = binary_operator<L, R, conc_string>; struct add_string { - operator std::string() const { + serialize_result_type serialize() const { return "+"; } }; @@ -1650,7 +1915,7 @@ namespace sqlite_orm { using add_t = binary_operator<L, R, add_string, arithmetic_t, negatable_t>; struct sub_string { - operator std::string() const { + serialize_result_type serialize() const { return "-"; } }; @@ -1662,7 +1927,7 @@ namespace sqlite_orm { using sub_t = binary_operator<L, R, sub_string, arithmetic_t, negatable_t>; struct mul_string { - operator std::string() const { + serialize_result_type serialize() const { return "*"; } }; @@ -1674,7 +1939,7 @@ namespace sqlite_orm { using mul_t = binary_operator<L, R, mul_string, arithmetic_t, negatable_t>; struct div_string { - operator std::string() const { + serialize_result_type serialize() const { return "/"; } }; @@ -1686,7 +1951,7 @@ namespace sqlite_orm { using div_t = binary_operator<L, R, div_string, arithmetic_t, negatable_t>; struct mod_operator_string { - operator std::string() const { + serialize_result_type serialize() const { return "%"; } }; @@ -1698,7 +1963,7 @@ namespace sqlite_orm { using mod_t = binary_operator<L, R, mod_operator_string, arithmetic_t, negatable_t>; struct bitwise_shift_left_string { - operator std::string() const { + serialize_result_type serialize() const { return "<<"; } }; @@ -1710,7 +1975,7 @@ namespace sqlite_orm { using bitwise_shift_left_t = binary_operator<L, R, bitwise_shift_left_string, arithmetic_t, negatable_t>; struct bitwise_shift_right_string { - operator std::string() const { + serialize_result_type serialize() const { return ">>"; } }; @@ -1722,7 +1987,7 @@ namespace sqlite_orm { using bitwise_shift_right_t = binary_operator<L, R, bitwise_shift_right_string, arithmetic_t, negatable_t>; struct bitwise_and_string { - operator std::string() const { + serialize_result_type serialize() const { return "&"; } }; @@ -1734,7 +1999,7 @@ namespace sqlite_orm { using bitwise_and_t = binary_operator<L, R, bitwise_and_string, arithmetic_t, negatable_t>; struct bitwise_or_string { - operator std::string() const { + serialize_result_type serialize() const { return "|"; } }; @@ -1746,7 +2011,7 @@ namespace sqlite_orm { using bitwise_or_t = binary_operator<L, R, bitwise_or_string, arithmetic_t, negatable_t>; struct bitwise_not_string { - operator std::string() const { + serialize_result_type serialize() const { return "~"; } }; @@ -1764,7 +2029,7 @@ namespace sqlite_orm { }; struct assign_string { - operator std::string() const { + serialize_result_type serialize() const { return "="; } }; @@ -1879,292 +2144,248 @@ namespace sqlite_orm { #include <tuple> // std::tuple #include <string> // std::string #include <memory> // std::unique_ptr -#include <type_traits> // std::true_type, std::false_type, std::is_same, std::enable_if, std::is_member_pointer, std::is_member_function_pointer +#include <type_traits> // std::is_same, std::is_member_object_pointer -// #include "type_is_nullable.h" +// #include "functional/cxx_universal.h" -// #include "tuple_helper/tuple_helper.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "default_value_extractor.h" +// #include "tuple_helper/tuple_traits.h" -// #include "constraints.h" +// #include "tuple_helper/tuple_filter.h" + +// #include "type_traits.h" // #include "member_traits/member_traits.h" -#include <type_traits> // std::enable_if +#include <type_traits> // std::enable_if, std::is_function, std::true_type, std::false_type -// #include "is_field_member_pointer.h" +// #include "../functional/cxx_universal.h" -#include <type_traits> // std::false_type, std::true_type, std::is_member_pointer, std::is_member_function_pointer +// #include "../functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { + // SFINAE friendly trait to get a member object pointer's field type + template<class T> + struct object_field_type {}; - template<class T, class SFINAE = void> - struct is_field_member_pointer : std::false_type {}; + template<class T> + using object_field_type_t = typename object_field_type<T>::type; + + template<class F, class O> + struct object_field_type<F O::*> : std::enable_if<!std::is_function<F>::value, F> {}; + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified return type) template<class T> - struct is_field_member_pointer<T, - typename std::enable_if<std::is_member_pointer<T>::value && - !std::is_member_function_pointer<T>::value>::type> - : std::true_type {}; - } -} + struct getter_field_type {}; -// #include "is_getter.h" + template<class T> + using getter_field_type_t = typename getter_field_type<T>::type; -// #include "field_member_traits.h" + template<class T, class O> + struct getter_field_type<T O::*> : getter_field_type<T> {}; -#include <type_traits> // std::enable_if + template<class F> + struct getter_field_type<F(void) const> : polyfill::remove_cvref<F> {}; -// #include "is_field_member_pointer.h" + template<class F> + struct getter_field_type<F(void)> : polyfill::remove_cvref<F> {}; -namespace sqlite_orm { - namespace internal { +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class F> + struct getter_field_type<F(void) const noexcept> : polyfill::remove_cvref<F> {}; - template<class T, class SFINAE = void> - struct field_member_traits; + template<class F> + struct getter_field_type<F(void) noexcept> : polyfill::remove_cvref<F> {}; +#endif - template<class O, class F> - struct field_member_traits<F O::*, typename std::enable_if<is_field_member_pointer<F O::*>::value>::type> { - using object_type = O; - using field_type = F; - }; - } -} + // SFINAE friendly trait to get a member function pointer's field type (i.e. unqualified parameter type) + template<class T> + struct setter_field_type {}; -// #include "is_setter.h" + template<class T> + using setter_field_type_t = typename setter_field_type<T>::type; -// #include "getter_traits.h" + template<class T, class O> + struct setter_field_type<T O::*> : setter_field_type<T> {}; -// #include "setter_traits.h" + template<class F> + struct setter_field_type<void(F)> : polyfill::remove_cvref<F> {}; -namespace sqlite_orm { - namespace internal { +#ifdef SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED + template<class F> + struct setter_field_type<void(F) noexcept> : polyfill::remove_cvref<F> {}; +#endif template<class T, class SFINAE = void> - struct member_traits; + struct is_getter : std::false_type {}; + template<class T> + struct is_getter<T, polyfill::void_t<getter_field_type_t<T>>> : std::true_type {}; template<class T> - struct member_traits<T, typename std::enable_if<is_field_member_pointer<T>::value>::type> { - using object_type = typename field_member_traits<T>::object_type; - using field_type = typename field_member_traits<T>::field_type; - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_getter_v = is_getter<T>::value; + template<class T, class SFINAE = void> + struct is_setter : std::false_type {}; template<class T> - struct member_traits<T, typename std::enable_if<is_getter<T>::value>::type> { - using object_type = typename getter_traits<T>::object_type; - using field_type = typename getter_traits<T>::field_type; - }; + struct is_setter<T, polyfill::void_t<setter_field_type_t<T>>> : std::true_type {}; template<class T> - struct member_traits<T, typename std::enable_if<is_setter<T>::value>::type> { - using object_type = typename setter_traits<T>::object_type; - using field_type = typename setter_traits<T>::field_type; - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_setter_v = is_setter<T>::value; + + template<class T> + struct member_field_type : object_field_type<T>, getter_field_type<T>, setter_field_type<T> {}; + + template<class T> + using member_field_type_t = typename member_field_type<T>::type; + + template<class T> + struct member_object_type {}; + + template<class F, class O> + struct member_object_type<F O::*> : polyfill::type_identity<O> {}; + + template<class T> + using member_object_type_t = typename member_object_type<T>::type; } } +// #include "type_is_nullable.h" + +// #include "constraints.h" + namespace sqlite_orm { namespace internal { - struct basic_column { + struct column_identifier { /** - * Column name. Specified during construction in `make_column`. + * Column name. */ - const std::string name; + std::string name; }; - /** - * This class stores single column info. column_t is a pair of [column_name:member_pointer] mapped to a storage - * O is a mapped class, e.g. User - * T is a mapped class'es field type, e.g. &User::name - * Op... is a constraints pack, e.g. primary_key_t, autoincrement_t etc + struct empty_setter {}; + + /* + * Encapsulates object member pointers that are used as column fields, + * and whose object is mapped to storage. + * + * G is a member object pointer or member function pointer + * S is a member function pointer or `empty_setter` */ - template<class O, class T, class G /* = const T& (O::*)() const*/, class S /* = void (O::*)(T)*/, class... Op> - struct column_t : basic_column { - using object_type = O; - using field_type = T; - using constraints_type = std::tuple<Op...>; - using member_pointer_t = field_type object_type::*; - using getter_type = G; + template<class G, class S> + struct column_field { + using member_pointer_t = G; using setter_type = S; + using object_type = member_object_type_t<G>; + using field_type = member_field_type_t<G>; /** - * Member pointer used to read/write member + * Member pointer used to read a field value. + * If it is a object member pointer it is also used to write a field value. */ - member_pointer_t member_pointer /* = nullptr*/; + const member_pointer_t member_pointer; /** - * Getter member function pointer to get a value. If member_pointer is null than - * `getter` and `setter` must be not null + * Setter member function to write a field value */ - getter_type getter /* = nullptr*/; - - /** - * Setter member function - */ - setter_type setter /* = nullptr*/; - - /** - * Constraints tuple - */ - constraints_type constraints; - - column_t(std::string name_, - member_pointer_t member_pointer_, - getter_type getter_, - setter_type setter_, - constraints_type constraints_) : - basic_column{move(name_)}, - member_pointer(member_pointer_), getter(getter_), setter(setter_), constraints(move(constraints_)) {} + SQLITE_ORM_NOUNIQUEADDRESS + const setter_type setter; /** * Simplified interface for `NOT NULL` constraint */ - bool not_null() const { + constexpr bool is_not_null() const { return !type_is_nullable<field_type>::value; } + }; - template<class Opt> - constexpr bool has() const { - return tuple_helper::tuple_contains_type<Opt, constraints_type>::value; - } + /* + * Encapsulates a tuple of column constraints. + * + * Op... is a constraints pack, e.g. primary_key_t, unique_t etc + */ + template<class... Op> + struct column_constraints { + using constraints_type = std::tuple<Op...>; - template<class O1, class O2, class... Opts> - constexpr bool has_every() const { - if(has<O1>() && has<O2>()) { - return true; - } else { - return has_every<Opts...>(); - } + SQLITE_ORM_NOUNIQUEADDRESS + constraints_type constraints; + + /** + * Checks whether contraints are of trait `Trait` + */ + template<template<class...> class Trait> + constexpr bool is() const { + return tuple_has<Trait, constraints_type>::value; } - template<class O1> - constexpr bool has_every() const { - return has<O1>(); + constexpr bool is_generated() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + return is<is_generated_always>(); +#else + return false; +#endif } /** * Simplified interface for `DEFAULT` constraint * @return string representation of default value if it exists otherwise nullptr */ - std::unique_ptr<std::string> default_value() const { - std::unique_ptr<std::string> res; - iterate_tuple(this->constraints, [&res](auto& v) { - auto dft = internal::default_value_extractor()(v); - if(dft) { - res = std::move(dft); - } - }); - return res; - } + std::unique_ptr<std::string> default_value() const; }; - // we are compelled to wrap all sfinae-implemented traits to prevent "error: type/value mismatch at argument 2 in template parameter list" - namespace sfinae { - /** - * Column with insertable primary key traits. Common case. - */ - template<class T, class SFINAE = void> - struct is_column_with_insertable_primary_key : public std::false_type {}; - - /** - * Column with insertable primary key traits. Specialized case case. - */ - template<class O, class T, class... Op> - struct is_column_with_insertable_primary_key< - column_t<O, T, Op...>, - typename std::enable_if<(tuple_helper::tuple_contains_type< - primary_key_t<>, - typename column_t<O, T, Op...>::constraints_type>::value)>::type> { - using column_type = column_t<O, T, Op...>; - static constexpr bool value = is_primary_key_insertable<column_type>::value; - }; - - /** - * Column with noninsertable primary key traits. Common case. - */ - template<class T, class SFINAE = void> - struct is_column_with_noninsertable_primary_key : public std::false_type {}; - - /** - * Column with noninsertable primary key traits. Specialized case case. - */ - template<class O, class T, class... Op> - struct is_column_with_noninsertable_primary_key< - column_t<O, T, Op...>, - typename std::enable_if<(tuple_helper::tuple_contains_type< - primary_key_t<>, - typename column_t<O, T, Op...>::constraints_type>::value)>::type> { - using column_type = column_t<O, T, Op...>; - static constexpr bool value = !is_primary_key_insertable<column_type>::value; - }; - - } - - /** - * Column traits. Common case. - */ - template<class T> - struct is_column : public std::false_type {}; - /** - * Column traits. Specialized case case. + * Column definition. + * + * It is a composition of orthogonal information stored in different base classes. */ - template<class O, class T, class... Op> - struct is_column<column_t<O, T, Op...>> : public std::true_type {}; + template<class G, class S, class... Op> + struct column_t : column_identifier, column_field<G, S>, column_constraints<Op...> { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + column_t(std::string name, G memberPointer, S setter, std::tuple<Op...> op) : + column_identifier{move(name)}, column_field<G, S>{memberPointer, setter}, column_constraints<Op...>{ + move(op)} {} +#endif + }; - /** - * Column with insertable primary key traits. - */ template<class T> - struct is_column_with_insertable_primary_key : public sfinae::is_column_with_insertable_primary_key<T> {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = polyfill::is_specialization_of_v<T, column_t>; - /** - * Column with noninsertable primary key traits. - */ template<class T> - struct is_column_with_noninsertable_primary_key : public sfinae::is_column_with_noninsertable_primary_key<T> {}; + using is_column = polyfill::bool_constant<is_column_v<T>>; - template<class T> - struct column_field_type { - using type = void; - }; - - template<class O, class T, class... Op> - struct column_field_type<column_t<O, T, Op...>> { - using type = typename column_t<O, T, Op...>::field_type; - }; - - template<class T> - struct column_constraints_type { - using type = std::tuple<>; - }; + template<class Elements, class F> + using col_index_sequence_with_field_type = + filter_tuple_sequence_t<Elements, + check_if_is_type<F>::template fn, + field_type_t, + filter_tuple_sequence_t<Elements, is_column>>; - template<class O, class T, class... Op> - struct column_constraints_type<column_t<O, T, Op...>> { - using type = typename column_t<O, T, Op...>::constraints_type; - }; + template<class Elements, template<class...> class TraitFn> + using col_index_sequence_with = filter_tuple_sequence_t<Elements, + check_if_tuple_has<TraitFn>::template fn, + constraints_type_t, + filter_tuple_sequence_t<Elements, is_column>>; + template<class Elements, template<class...> class TraitFn> + using col_index_sequence_excluding = filter_tuple_sequence_t<Elements, + check_if_tuple_has_not<TraitFn>::template fn, + constraints_type_t, + filter_tuple_sequence_t<Elements, is_column>>; } /** * Column builder function. You should use it to create columns instead of constructor */ - template<class O, - class T, - typename = typename std::enable_if<!std::is_member_function_pointer<T O::*>::value>::type, - class... Op> - internal::column_t<O, T, const T& (O::*)() const, void (O::*)(T), Op...> - make_column(const std::string& name, T O::*m, Op... constraints) { - static_assert(internal::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, - "Incorrect constraints pack"); - static_assert(internal::is_field_member_pointer<T O::*>::value, - "second argument expected as a member field pointer, not member function pointer"); - return {name, m, nullptr, nullptr, std::make_tuple(constraints...)}; + template<class M, class... Op, internal::satisfies<std::is_member_object_pointer, M> = true> + internal::column_t<M, internal::empty_setter, Op...> make_column(std::string name, M m, Op... constraints) { + static_assert(polyfill::conjunction_v<internal::is_constraint<Op>...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), m, {}, std::make_tuple(constraints...)}); } /** @@ -2172,21 +2393,15 @@ namespace sqlite_orm { */ template<class G, class S, - typename = typename std::enable_if<internal::is_getter<G>::value>::type, - typename = typename std::enable_if<internal::is_setter<S>::value>::type, - class... Op> - internal::column_t<typename internal::setter_traits<S>::object_type, - typename internal::setter_traits<S>::field_type, - G, - S, - Op...> - make_column(const std::string& name, S setter, G getter, Op... constraints) { - static_assert(std::is_same<typename internal::setter_traits<S>::field_type, - typename internal::getter_traits<G>::field_type>::value, + class... Op, + internal::satisfies<internal::is_getter, G> = true, + internal::satisfies<internal::is_setter, S> = true> + internal::column_t<G, S, Op...> make_column(std::string name, S setter, G getter, Op... constraints) { + static_assert(std::is_same<internal::setter_field_type_t<S>, internal::getter_field_type_t<G>>::value, "Getter and setter must get and set same data type"); - static_assert(internal::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, - "Incorrect constraints pack"); - return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; + static_assert(polyfill::conjunction_v<internal::is_constraint<Op>...>, "Incorrect constraints pack"); + + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), getter, setter, std::make_tuple(constraints...)}); } /** @@ -2195,34 +2410,34 @@ namespace sqlite_orm { */ template<class G, class S, - typename = typename std::enable_if<internal::is_getter<G>::value>::type, - typename = typename std::enable_if<internal::is_setter<S>::value>::type, - class... Op> - internal::column_t<typename internal::setter_traits<S>::object_type, - typename internal::setter_traits<S>::field_type, - G, - S, - Op...> - make_column(const std::string& name, G getter, S setter, Op... constraints) { - static_assert(std::is_same<typename internal::setter_traits<S>::field_type, - typename internal::getter_traits<G>::field_type>::value, + class... Op, + internal::satisfies<internal::is_getter, G> = true, + internal::satisfies<internal::is_setter, S> = true> + internal::column_t<G, S, Op...> make_column(std::string name, G getter, S setter, Op... constraints) { + static_assert(std::is_same<internal::setter_field_type_t<S>, internal::getter_field_type_t<G>>::value, "Getter and setter must get and set same data type"); - static_assert(internal::template constraints_size<Op...>::value == std::tuple_size<std::tuple<Op...>>::value, - "Incorrect constraints pack"); - return {name, nullptr, getter, setter, std::make_tuple(constraints...)}; - } + static_assert(polyfill::conjunction_v<internal::is_constraint<Op>...>, "Incorrect constraints pack"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), getter, setter, std::make_tuple(constraints...)}); + } } #pragma once +#include <locale> // std::wstring_convert #include <string> // std::string #include <sstream> // std::stringstream #include <vector> // std::vector -#include <cstddef> // std::nullptr_t #include <memory> // std::shared_ptr, std::unique_ptr -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifndef SQLITE_ORM_OMITS_CODECVT +#include <codecvt> // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "is_std_ptr.h" namespace sqlite_orm { @@ -2230,12 +2445,26 @@ namespace sqlite_orm { * Is used to print members mapped to objects in storage_t::dump member function. * Other developers can create own specialization to map custom types */ - template<class T, typename Enable = void> - struct field_printer { + template<class T, typename SFINAE = void> + struct field_printer; + + namespace internal { + template<class T, class SFINAE = void> + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false; + template<class T> + SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v<T, polyfill::void_t<decltype(field_printer<T>{})>> = true + // Also see implementation note for `is_bindable_v` + ; + template<class T> + using is_printable = polyfill::bool_constant<is_printable_v<T>>; + } + + template<class T> + struct field_printer<T, std::enable_if_t<std::is_arithmetic<T>::value>> { std::string operator()(const T& t) const { - std::stringstream stream; - stream << t; - return stream.str(); + std::stringstream ss; + ss << t; + return ss.str(); } }; @@ -2245,9 +2474,9 @@ namespace sqlite_orm { template<> struct field_printer<unsigned char, void> { std::string operator()(const unsigned char& t) const { - std::stringstream stream; - stream << +t; - return stream.str(); + std::stringstream ss; + ss << +t; + return ss.str(); } }; @@ -2257,28 +2486,28 @@ namespace sqlite_orm { template<> struct field_printer<signed char, void> { std::string operator()(const signed char& t) const { - std::stringstream stream; - stream << +t; - return stream.str(); + std::stringstream ss; + ss << +t; + return ss.str(); } }; /** - * char is neigher signer char nor unsigned char so it has its own specialization + * char is neither signed char nor unsigned char so it has its own specialization */ template<> struct field_printer<char, void> { std::string operator()(const char& t) const { - std::stringstream stream; - stream << +t; - return stream.str(); + std::stringstream ss; + ss << +t; + return ss.str(); } }; - template<> - struct field_printer<std::string, void> { - std::string operator()(const std::string& t) const { - return t; + template<class T> + struct field_printer<T, std::enable_if_t<std::is_base_of<std::string, T>::value>> { + std::string operator()(std::string string) const { + return string; } }; @@ -2293,10 +2522,21 @@ namespace sqlite_orm { return ss.str(); } }; - +#ifndef SQLITE_ORM_OMITS_CODECVT + /** + * Specialization for std::wstring (UTF-16 assumed). + */ + template<class T> + struct field_printer<T, std::enable_if_t<std::is_base_of<std::wstring, T>::value>> { + std::string operator()(const std::wstring& wideString) const { + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; + return converter.to_bytes(wideString); + } + }; +#endif // SQLITE_ORM_OMITS_CODECVT template<> - struct field_printer<std::nullptr_t, void> { - std::string operator()(const std::nullptr_t&) const { + struct field_printer<nullptr_t, void> { + std::string operator()(const nullptr_t&) const { return "null"; } }; @@ -2309,35 +2549,34 @@ namespace sqlite_orm { }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct field_printer<std::shared_ptr<T>, void> { - std::string operator()(const std::shared_ptr<T>& t) const { - if(t) { - return field_printer<T>()(*t); - } else { - return field_printer<std::nullptr_t>()(nullptr); - } - } - }; + struct field_printer< + T, + std::enable_if_t<polyfill::conjunction_v<is_std_ptr<T>, + internal::is_printable<std::remove_cv_t<typename T::element_type>>>>> { + using unqualified_type = std::remove_cv_t<typename T::element_type>; - template<class T> - struct field_printer<std::unique_ptr<T>, void> { - std::string operator()(const std::unique_ptr<T>& t) const { + std::string operator()(const T& t) const { if(t) { - return field_printer<T>()(*t); + return field_printer<unqualified_type>()(*t); } else { - return field_printer<std::nullptr_t>()(nullptr); + return field_printer<nullptr_t>{}(nullptr); } } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct field_printer<std::optional<T>, void> { - std::string operator()(const std::optional<T>& t) const { + struct field_printer< + T, + std::enable_if_t<polyfill::conjunction_v<polyfill::is_specialization_of<T, std::optional>, + internal::is_printable<std::remove_cv_t<typename T::value_type>>>>> { + using unqualified_type = std::remove_cv_t<typename T::value_type>; + + std::string operator()(const T& t) const { if(t.has_value()) { - return field_printer<T>()(*t); + return field_printer<unqualified_type>()(*t); } else { - return field_printer<std::nullptr_t>()(nullptr); + return field_printer<std::nullopt_t>{}(std::nullopt); } } }; @@ -2351,6 +2590,12 @@ namespace sqlite_orm { #include <tuple> // std::tuple, std::tuple_size #include <sstream> // std::stringstream +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + // #include "collate_argument.h" // #include "constraints.h" @@ -2389,9 +2634,55 @@ namespace sqlite_orm { } } +// #include "serializer_context.h" + +namespace sqlite_orm { + + namespace internal { + + struct serializer_context_base { + bool replace_bindable_with_question = false; + bool skip_table_name = true; + bool use_parentheses = true; + }; + + template<class DBOs> + struct serializer_context : serializer_context_base { + using db_objects_type = DBOs; + + const db_objects_type& db_objects; + + serializer_context(const db_objects_type& dbObjects) : db_objects{dbObjects} {} + }; + + template<class S> + struct serializer_context_builder { + using storage_type = S; + using db_objects_type = typename storage_type::db_objects_type; + + serializer_context_builder(const storage_type& storage_) : storage{storage_} {} + + serializer_context<db_objects_type> operator()() const { + return {obtain_db_objects(this->storage)}; + } + + const storage_type& storage; + }; + + } + +} + // #include "tags.h" // #include "expression.h" + +#include <tuple> +#include <utility> // std::move, std::forward +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + // #include "operators.h" namespace sqlite_orm { @@ -2418,7 +2709,7 @@ namespace sqlite_orm { return {this->value, std::move(r)}; } - assign_t<T, std::nullptr_t> operator=(std::nullptr_t) const { + assign_t<T, nullptr_t> operator=(nullptr_t) const { return {this->value, nullptr}; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -2468,6 +2759,26 @@ namespace sqlite_orm { } } +// #include "type_printer.h" + +// #include "literal.h" + +namespace sqlite_orm { + namespace internal { + + /* + * Protect an otherwise bindable element so that it is always serialized as a literal value. + */ + template<class T> + struct literal_holder { + using type = T; + + type value; + }; + + } +} + namespace sqlite_orm { namespace internal { @@ -2508,10 +2819,7 @@ namespace sqlite_orm { }; template<class T> - struct is_offset : std::false_type {}; - - template<class T> - struct is_offset<offset_t<T>> : std::true_type {}; + using is_offset = polyfill::is_specialization_of<T, offset_t>; /** * Collated something @@ -2659,10 +2967,8 @@ namespace sqlite_orm { template<class C> named_collate<self> collate() const { std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - return {*this, std::move(name)}; + ss << C::name() << std::flush; + return {*this, ss.str()}; } }; @@ -2809,13 +3115,9 @@ namespace sqlite_orm { struct in_base { bool negative = false; // used in not_in - operator std::string() const { - if(!this->negative) { - return "IN"; - } else { - return "NOT IN"; - } - } +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + in_base(bool negative) : negative{negative} {} +#endif }; /** @@ -2881,10 +3183,12 @@ namespace sqlite_orm { int asc_desc = 0; // 1: asc, -1: desc std::string _collate_argument; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED order_by_base() = default; order_by_base(decltype(asc_desc) asc_desc_, decltype(_collate_argument) _collate_argument_) : asc_desc(asc_desc_), _collate_argument(move(_collate_argument_)) {} +#endif }; struct order_by_string { @@ -2905,13 +3209,13 @@ namespace sqlite_orm { order_by_t(expression_type expression_) : order_by_base(), expression(std::move(expression_)) {} - self asc() { + self asc() const { auto res = *this; res.asc_desc = 1; return res; } - self desc() { + self desc() const { auto res = *this; res.asc_desc = -1; return res; @@ -2919,22 +3223,19 @@ namespace sqlite_orm { self collate_binary() const { auto res = *this; - res._collate_argument = - collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::binary); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::binary); return res; } self collate_nocase() const { auto res = *this; - res._collate_argument = - collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::nocase); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::nocase); return res; } self collate_rtrim() const { auto res = *this; - res._collate_argument = - collate_constraint_t::string_from_collate_argument(sqlite_orm::internal::collate_argument::rtrim); + res._collate_argument = collate_constraint_t::string_from_collate_argument(collate_argument::rtrim); return res; } @@ -2947,10 +3248,8 @@ namespace sqlite_orm { template<class C> self collate() const { std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - return this->collate(move(name)); + ss << C::name() << std::flush; + return this->collate(ss.str()); } }; @@ -2963,7 +3262,7 @@ namespace sqlite_orm { args_type args; - multi_order_by_t(args_type&& args_) : args(std::move(args_)) {} + multi_order_by_t(args_type args_) : args{std::move(args_)} {} }; struct dynamic_order_by_entry_t : order_by_base { @@ -2974,7 +3273,7 @@ namespace sqlite_orm { }; /** - * C - serializator context class + * C - serializer context class */ template<class C> struct dynamic_order_by_t : order_by_string { @@ -3010,39 +3309,13 @@ namespace sqlite_orm { }; template<class T> - struct is_order_by : std::false_type {}; - - template<class O> - struct is_order_by<order_by_t<O>> : std::true_type {}; - - template<class... Args> - struct is_order_by<multi_order_by_t<Args...>> : std::true_type {}; - - template<class C> - struct is_order_by<dynamic_order_by_t<C>> : std::true_type {}; - - struct group_by_string { - operator std::string() const { - return "GROUP BY"; - } - }; - - /** - * GROUP BY pack holder. - */ - template<class... Args> - struct group_by_t : group_by_string { - using args_type = std::tuple<Args...>; - args_type args; - - group_by_t(args_type&& args_) : args(std::move(args_)) {} - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_order_by_v = + polyfill::disjunction_v<polyfill::is_specialization_of<T, order_by_t>, + polyfill::is_specialization_of<T, multi_order_by_t>, + polyfill::is_specialization_of<T, dynamic_order_by_t>>; template<class T> - struct is_group_by : std::false_type {}; - - template<class... Args> - struct is_group_by<group_by_t<Args...>> : std::true_type {}; + using is_order_by = polyfill::bool_constant<is_order_by_v<T>>; struct between_string { operator std::string() const { @@ -3230,9 +3503,9 @@ namespace sqlite_orm { /** * USING argument holder. */ - template<class F, class O> + template<class T, class M> struct using_t { - F O::*column = nullptr; + column_pointer<T, M> column; operator std::string() const { return "USING"; @@ -3260,47 +3533,6 @@ namespace sqlite_orm { inner_join_t(on_type constraint_) : constraint(std::move(constraint_)) {} }; - struct exists_string { - operator std::string() const { - return "EXISTS"; - } - }; - - template<class T> - struct exists_t : condition_t, exists_string, internal::negatable_t { - using type = T; - using self = exists_t<type>; - - type t; - - exists_t(T t_) : t(std::move(t_)) {} - }; - - struct having_string { - operator std::string() const { - return "HAVING"; - } - }; - - /** - * HAVING holder. - * T is having argument type. - */ - template<class T> - struct having_t : having_string { - using type = T; - - type t; - - having_t(type t_) : t(std::move(t_)) {} - }; - - template<class T> - struct is_having : std::false_type {}; - - template<class T> - struct is_having<having_t<T>> : std::true_type {}; - struct cast_string { operator std::string() const { return "CAST"; @@ -3329,10 +3561,7 @@ namespace sqlite_orm { }; template<class T> - struct is_from : std::false_type {}; - - template<class... Args> - struct is_from<from_t<Args...>> : std::true_type {}; + using is_from = polyfill::is_specialization_of<T, from_t>; } /** @@ -3345,7 +3574,7 @@ namespace sqlite_orm { return {}; } - template<class T, typename = typename std::enable_if<std::is_base_of<internal::negatable_t, T>::value>::type> + template<class T, internal::satisfies<std::is_base_of, internal::negatable_t, T> = true> internal::negated_condition_t<T> operator!(T arg) { return {std::move(arg)}; } @@ -3504,8 +3733,12 @@ namespace sqlite_orm { } template<class F, class O> - internal::using_t<F, O> using_(F O::*p) { - return {std::move(p)}; + internal::using_t<O, F O::*> using_(F O::*p) { + return {p}; + } + template<class T, class M> + internal::using_t<T, M> using_(internal::column_pointer<T, M> cp) { + return {std::move(cp)}; } template<class T> @@ -3553,9 +3786,8 @@ namespace sqlite_orm { return {std::move(lim)}; } - template<class T, class O> - typename std::enable_if<!internal::is_offset<T>::value, internal::limit_t<T, true, true, O>>::type limit(O off, - T lim) { + template<class T, class O, internal::satisfies_not<internal::is_offset, T> = true> + internal::limit_t<T, true, true, O> limit(O off, T lim) { return {std::move(lim), {std::move(off)}}; } @@ -3566,8 +3798,9 @@ namespace sqlite_orm { template<class L, class R, - typename = typename std::enable_if<std::is_base_of<internal::condition_t, L>::value || - std::is_base_of<internal::condition_t, R>::value>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::condition_t, L>, + std::is_base_of<internal::condition_t, R>>, + bool> = true> auto operator&&(L l, R r) { using internal::get_from_expression; return internal::make_and_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); @@ -3581,8 +3814,9 @@ namespace sqlite_orm { template<class L, class R, - typename = typename std::enable_if<std::is_base_of<internal::condition_t, L>::value || - std::is_base_of<internal::condition_t, R>::value>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::condition_t, L>, + std::is_base_of<internal::condition_t, R>>, + bool> = true> auto operator||(L l, R r) { using internal::get_from_expression; return internal::make_or_condition(std::move(get_from_expression(l)), std::move(get_from_expression(r))); @@ -3695,15 +3929,29 @@ namespace sqlite_orm { } /** - * ORDER BY column - * Example: storage.select(&User::name, order_by(&User::id)) + * ORDER BY column, column alias or expression + * + * Examples: + * storage.select(&User::name, order_by(&User::id)) + * storage.select(as<colalias_a>(&User::name), order_by(get<colalias_a>())) */ - template<class O> + template<class O, internal::satisfies_not<std::is_base_of, integer_printer, type_printer<O>> = true> internal::order_by_t<O> order_by(O o) { return {std::move(o)}; } /** + * ORDER BY positional ordinal + * + * Examples: + * storage.select(&User::name, order_by(1)) + */ + template<class O, internal::satisfies<std::is_base_of, integer_printer, type_printer<O>> = true> + internal::order_by_t<internal::literal_holder<O>> order_by(O o) { + return {{std::move(o)}}; + } + + /** * ORDER BY column1, column2 * Example: storage.get_all<Singer>(multi_order_by(order_by(&Singer::name).asc(), order_by(&Singer::gender).desc()) */ @@ -3725,22 +3973,13 @@ namespace sqlite_orm { * } */ template<class S> - internal::dynamic_order_by_t<internal::serializator_context<typename S::impl_type>> + internal::dynamic_order_by_t<internal::serializer_context<typename S::db_objects_type>> dynamic_order_by(const S& storage) { - internal::serializator_context_builder<S> builder(storage); + internal::serializer_context_builder<S> builder(storage); return builder(); } /** - * GROUP BY column. - * Example: storage.get_all<Employee>(group_by(&Employee::name)) - */ - template<class... Args> - internal::group_by_t<Args...> group_by(Args&&... args) { - return {std::make_tuple(std::forward<Args>(args)...)}; - } - - /** * X BETWEEN Y AND Z * Example: storage.select(between(&User::id, 10, 20)) */ @@ -3777,28 +4016,6 @@ namespace sqlite_orm { } /** - * EXISTS(condition). - * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), - where(exists(select(asterisk<Customer>(), - where(is_equal(&Customer::grade, 3) and - is_equal(&Agent::code, &Customer::agentCode))))), - order_by(&Agent::comission)); - */ - template<class T> - internal::exists_t<T> exists(T t) { - return {std::move(t)}; - } - - /** - * HAVING(expression). - * Example: storage.get_all<Employee>(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); - */ - template<class T> - internal::having_t<T> having(T t) { - return {std::move(t)}; - } - - /** * CAST(X AS type). * Example: cast<std::string>(&User::id) */ @@ -3816,7 +4033,7 @@ namespace sqlite_orm { namespace sqlite_orm { /** - * This is base class for every class which is used as a custom table alias. + * This is base class for every class which is used as a custom table alias, column alias or expression alias. * For more information please look through self_join.cpp example */ struct alias_tag {}; @@ -3825,7 +4042,7 @@ namespace sqlite_orm { /** * This is a common built-in class used for custom single character table aliases. - * Also you can use language aliases `alias_a`, `alias_b` etc. instead + * For convenience there exist type aliases `alias_a`, `alias_b`, ... */ template<class T, char A> struct table_alias : alias_tag { @@ -3845,28 +4062,21 @@ namespace sqlite_orm { using column_type = C; column_type column; - - alias_column_t(){}; - - alias_column_t(column_type column_) : column(std::move(column_)) {} }; template<class T, class SFINAE = void> - struct alias_extractor; - - template<class T> - struct alias_extractor<T, typename std::enable_if<std::is_base_of<alias_tag, T>::value>::type> { + struct alias_extractor { static std::string get() { - std::stringstream ss; - ss << T::get(); - return ss.str(); + return {}; } }; template<class T> - struct alias_extractor<T, typename std::enable_if<!std::is_base_of<alias_tag, T>::value>::type> { + struct alias_extractor<T, std::enable_if_t<std::is_base_of<alias_tag, T>::value>> { static std::string get() { - return {}; + std::stringstream ss; + ss << T::get(); + return ss.str(); } }; @@ -3881,6 +4091,17 @@ namespace sqlite_orm { expression_type expression; }; + /** + * This is a common built-in class used for custom single-character column aliases. + * For convenience there exist type aliases `colalias_a`, `colalias_b`, ... + */ + template<char A> + struct column_alias : alias_tag { + static std::string get() { + return std::string(1u, A); + } + }; + template<class T> struct alias_holder { using type = T; @@ -3960,6 +4181,16 @@ namespace sqlite_orm { using alias_y = internal::table_alias<T, 'y'>; template<class T> using alias_z = internal::table_alias<T, 'z'>; + + using colalias_a = internal::column_alias<'a'>; + using colalias_b = internal::column_alias<'b'>; + using colalias_c = internal::column_alias<'c'>; + using colalias_d = internal::column_alias<'d'>; + using colalias_e = internal::column_alias<'e'>; + using colalias_f = internal::column_alias<'f'>; + using colalias_g = internal::column_alias<'g'>; + using colalias_h = internal::column_alias<'h'>; + using colalias_i = internal::column_alias<'i'>; } #pragma once @@ -4072,9 +4303,9 @@ namespace sqlite_orm { #include <memory> // std::unique_ptr #include <vector> // std::vector -// #include "conditions.h" +// #include "functional/cxx_type_traits_polyfill.h" -// #include "operators.h" +// #include "conditions.h" // #include "is_base_of_template.h" @@ -4088,48 +4319,58 @@ namespace sqlite_orm { * This is because of bug in MSVC, for more information, please visit * https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753 */ -#if defined(_MSC_VER) - template<template<typename...> class Base, typename Derived> +#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template<template<typename...> class Base> struct is_base_of_template_impl { template<typename... Ts> - static constexpr std::true_type test(const Base<Ts...>*); + static constexpr std::true_type test(const Base<Ts...>&); static constexpr std::false_type test(...); - - using type = decltype(test(std::declval<Derived*>())); }; - template<typename Derived, template<typename...> class Base> - using is_base_of_template = typename is_base_of_template_impl<Base, Derived>::type; - + template<typename T, template<typename...> class C> + using is_base_of_template = decltype(is_base_of_template_impl<C>::test(std::declval<T>())); #else template<template<typename...> class C, typename... Ts> - std::true_type is_base_of_template_impl(const C<Ts...>*); + std::true_type is_base_of_template_impl(const C<Ts...>&); template<template<typename...> class C> std::false_type is_base_of_template_impl(...); template<typename T, template<typename...> class C> - using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>())); + using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T>())); #endif + + template<typename T, template<typename...> class C> + SQLITE_ORM_INLINE_VAR constexpr bool is_base_of_template_v = is_base_of_template<T, C>::value; } } +// #include "tuple_helper/tuple_filter.h" + // #include "serialize_result_type.h" -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include <string_view> // string_view -#else -#include <string> // std::string -#endif +// #include "operators.h" + +// #include "ast/into.h" + +// #include "../functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { namespace internal { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - using serialize_result_type = std::string_view; -#else - using serialize_result_type = std::string; -#endif + + template<class T> + struct into_t { + using type = T; + }; + + template<class T> + using is_into = polyfill::is_specialization_of<T, into_t>; + } + + template<class T> + internal::into_t<T> into() { + return {}; } } @@ -4141,9 +4382,6 @@ namespace sqlite_orm { namespace internal { template<class T> - struct is_into; - - template<class T> struct unique_ptr_result_of {}; /** @@ -4158,13 +4396,37 @@ namespace sqlite_orm { using string_type = S; using args_type = std::tuple<Args...>; - static constexpr const size_t args_size = std::tuple_size<args_type>::value; + static constexpr size_t args_size = std::tuple_size<args_type>::value; args_type args; built_in_function_t(args_type&& args_) : args(std::move(args_)) {} }; + template<class F, class W> + struct filtered_aggregate_function { + using function_type = F; + using where_expression = W; + + function_type function; + where_expression where; + }; + + template<class C> + struct where_t; + + template<class R, class S, class... Args> + struct built_in_aggregate_function_t : built_in_function_t<R, S, Args...> { + using super = built_in_function_t<R, S, Args...>; + + using super::super; + + template<class W> + filtered_aggregate_function<built_in_aggregate_function_t<R, S, Args...>, W> filter(where_t<W> wh) { + return {*this, std::move(wh.expression)}; + } + }; + struct typeof_string { serialize_result_type serialize() const { return "TYPEOF"; @@ -4301,6 +4563,12 @@ namespace sqlite_orm { } }; + struct nullif_string { + serialize_result_type serialize() const { + return "NULLIF"; + } + }; + struct date_string { serialize_result_type serialize() const { return "DATE"; @@ -4375,6 +4643,11 @@ namespace sqlite_orm { template<class T> struct count_asterisk_t : count_string { using type = T; + + template<class W> + filtered_aggregate_function<count_asterisk_t<T>, W> filter(where_t<W> wh) { + return {*this, std::move(wh.expression)}; + } }; /** @@ -4689,6 +4962,9 @@ namespace sqlite_orm { } }; #endif // SQLITE_ENABLE_JSON1 + + template<class T> + using field_type_or_type_t = polyfill::detected_or_t<T, type_t, member_field_type<T>>; } /** @@ -4696,48 +4972,42 @@ namespace sqlite_orm { */ template<class F, class R, - typename = - typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> + std::enable_if_t<internal::is_base_of_template_v<F, internal::built_in_function_t>, bool> = true> internal::lesser_than_t<F, R> operator<(F f, R r) { return {std::move(f), std::move(r)}; } template<class F, class R, - typename = - typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> + std::enable_if_t<internal::is_base_of_template_v<F, internal::built_in_function_t>, bool> = true> internal::lesser_or_equal_t<F, R> operator<=(F f, R r) { return {std::move(f), std::move(r)}; } template<class F, class R, - typename = - typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> + std::enable_if_t<internal::is_base_of_template_v<F, internal::built_in_function_t>, bool> = true> internal::greater_than_t<F, R> operator>(F f, R r) { return {std::move(f), std::move(r)}; } template<class F, class R, - typename = - typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> + std::enable_if_t<internal::is_base_of_template_v<F, internal::built_in_function_t>, bool> = true> internal::greater_or_equal_t<F, R> operator>=(F f, R r) { return {std::move(f), std::move(r)}; } template<class F, class R, - typename = - typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> + std::enable_if_t<internal::is_base_of_template_v<F, internal::built_in_function_t>, bool> = true> internal::is_equal_t<F, R> operator==(F f, R r) { return {std::move(f), std::move(r)}; } template<class F, class R, - typename = - typename std::enable_if<internal::is_base_of_template<F, internal::built_in_function_t>::value>::type> + std::enable_if_t<internal::is_base_of_template_v<F, internal::built_in_function_t>, bool> = true> internal::is_not_equal_t<F, R> operator!=(F f, R r) { return {std::move(f), std::move(r)}; } @@ -5734,10 +6004,11 @@ namespace sqlite_orm { /** * REPLACE(X) function https://sqlite.org/lang_corefunc.html#replace */ - template<class X, class Y, class Z> - typename std::enable_if<internal::count_tuple<std::tuple<X, Y, Z>, internal::is_into>::value == 0, - internal::built_in_function_t<std::string, internal::replace_string, X, Y, Z>>::type - replace(X x, Y y, Z z) { + template<class X, + class Y, + class Z, + std::enable_if_t<internal::count_tuple<std::tuple<X, Y, Z>, internal::is_into>::value == 0, bool> = true> + internal::built_in_function_t<std::string, internal::replace_string, X, Y, Z> replace(X x, Y y, Z z) { return {std::tuple<X, Y, Z>{std::forward<X>(x), std::forward<Y>(y), std::forward<Z>(z)}}; } @@ -5779,18 +6050,68 @@ namespace sqlite_orm { /** * COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce */ - template<class R, class... Args> - internal::built_in_function_t<R, internal::coalesce_string, Args...> coalesce(Args... args) { + template<class R = void, class... Args> + auto coalesce(Args... args) + -> internal::built_in_function_t<typename std::conditional_t< // choose R or common type + std::is_void<R>::value, + std::common_type<internal::field_type_or_type_t<Args>...>, + polyfill::type_identity<R>>::type, + internal::coalesce_string, + Args...> { return {std::make_tuple(std::forward<Args>(args)...)}; } /** * IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull */ + template<class R = void, class X, class Y> + auto ifnull(X x, Y y) -> internal::built_in_function_t< + typename std::conditional_t< // choose R or common type + std::is_void<R>::value, + std::common_type<internal::field_type_or_type_t<X>, internal::field_type_or_type_t<Y>>, + polyfill::type_identity<R>>::type, + internal::ifnull_string, + X, + Y> { + return {std::make_tuple(std::move(x), std::move(y))}; + } + + /** + * NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif + */ +#if defined(SQLITE_ORM_OPTIONAL_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + /** + * NULLIF(X,Y) using common return type of X and Y + */ + template<class R = void, + class X, + class Y, + std::enable_if_t<polyfill::disjunction_v<polyfill::negation<std::is_void<R>>, + polyfill::is_detected<std::common_type_t, + internal::field_type_or_type_t<X>, + internal::field_type_or_type_t<Y>>>, + bool> = true> + auto nullif(X x, Y y) { + if constexpr(std::is_void_v<R>) { + using F = internal::built_in_function_t< + std::optional<std::common_type_t<internal::field_type_or_type_t<X>, internal::field_type_or_type_t<Y>>>, + internal::nullif_string, + X, + Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } else { + using F = internal::built_in_function_t<R, internal::nullif_string, X, Y>; + + return F{std::make_tuple(std::move(x), std::move(y))}; + } + } +#else template<class R, class X, class Y> - internal::built_in_function_t<R, internal::ifnull_string, X, Y> ifnull(X x, Y y) { + internal::built_in_function_t<R, internal::nullif_string, X, Y> nullif(X x, Y y) { return {std::make_tuple(std::move(x), std::move(y))}; } +#endif /** * DATE(timestring, modifier, modifier, ...) function https://www.sqlite.org/lang_datefunc.html @@ -5861,7 +6182,7 @@ namespace sqlite_orm { * SOUNDEX(X) function https://www.sqlite.org/lang_corefunc.html#soundex */ template<class X> - internal::core_function_t<std::string, internal::soundex_string, X> soundex(X x) { + internal::built_in_function_t<std::string, internal::soundex_string, X> soundex(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } #endif @@ -5870,7 +6191,7 @@ namespace sqlite_orm { * TOTAL(X) aggregate function. */ template<class X> - internal::built_in_function_t<double, internal::total_string, X> total(X x) { + internal::built_in_aggregate_function_t<double, internal::total_string, X> total(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } @@ -5878,7 +6199,7 @@ namespace sqlite_orm { * SUM(X) aggregate function. */ template<class X> - internal::built_in_function_t<std::unique_ptr<double>, internal::sum_string, X> sum(X x) { + internal::built_in_aggregate_function_t<std::unique_ptr<double>, internal::sum_string, X> sum(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } @@ -5886,7 +6207,7 @@ namespace sqlite_orm { * COUNT(X) aggregate function. */ template<class X> - internal::built_in_function_t<int, internal::count_string, X> count(X x) { + internal::built_in_aggregate_function_t<int, internal::count_string, X> count(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } @@ -5910,7 +6231,7 @@ namespace sqlite_orm { * AVG(X) aggregate function. */ template<class X> - internal::built_in_function_t<double, internal::avg_string, X> avg(X x) { + internal::built_in_aggregate_function_t<double, internal::avg_string, X> avg(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } @@ -5918,7 +6239,7 @@ namespace sqlite_orm { * MAX(X) aggregate function. */ template<class X> - internal::built_in_function_t<internal::unique_ptr_result_of<X>, internal::max_string, X> max(X x) { + internal::built_in_aggregate_function_t<internal::unique_ptr_result_of<X>, internal::max_string, X> max(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } @@ -5926,15 +6247,35 @@ namespace sqlite_orm { * MIN(X) aggregate function. */ template<class X> - internal::built_in_function_t<internal::unique_ptr_result_of<X>, internal::min_string, X> min(X x) { + internal::built_in_aggregate_function_t<internal::unique_ptr_result_of<X>, internal::min_string, X> min(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } /** + * MAX(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template<class X, class Y, class... Rest> + internal::built_in_function_t<internal::unique_ptr_result_of<X>, internal::max_string, X, Y, Rest...> + max(X x, Y y, Rest... rest) { + return {std::tuple<X, Y, Rest...>{std::forward<X>(x), std::forward<Y>(y), std::forward<Rest>(rest)...}}; + } + + /** + * MIN(X, Y, ...) scalar function. + * The return type is the type of the first argument. + */ + template<class X, class Y, class... Rest> + internal::built_in_function_t<internal::unique_ptr_result_of<X>, internal::min_string, X, Y, Rest...> + min(X x, Y y, Rest... rest) { + return {std::tuple<X, Y, Rest...>{std::forward<X>(x), std::forward<Y>(y), std::forward<Rest>(rest)...}}; + } + + /** * GROUP_CONCAT(X) aggregate function. */ template<class X> - internal::built_in_function_t<std::string, internal::group_concat_string, X> group_concat(X x) { + internal::built_in_aggregate_function_t<std::string, internal::group_concat_string, X> group_concat(X x) { return {std::tuple<X>{std::forward<X>(x)}}; } @@ -5942,7 +6283,7 @@ namespace sqlite_orm { * GROUP_CONCAT(X, Y) aggregate function. */ template<class X, class Y> - internal::built_in_function_t<std::string, internal::group_concat_string, X, Y> group_concat(X x, Y y) { + internal::built_in_aggregate_function_t<std::string, internal::group_concat_string, X, Y> group_concat(X x, Y y) { return {std::tuple<X, Y>{std::forward<X>(x), std::forward<Y>(y)}}; } #ifdef SQLITE_ENABLE_JSON1 @@ -6070,45 +6411,45 @@ namespace sqlite_orm { #endif // SQLITE_ENABLE_JSON1 template<class L, class R, - typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + - std::is_base_of<internal::arithmetic_t, R>::value > - 0)>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::arithmetic_t, L>, + std::is_base_of<internal::arithmetic_t, R>>, + bool> = true> internal::add_t<L, R> operator+(L l, R r) { return {std::move(l), std::move(r)}; } template<class L, class R, - typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + - std::is_base_of<internal::arithmetic_t, R>::value > - 0)>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::arithmetic_t, L>, + std::is_base_of<internal::arithmetic_t, R>>, + bool> = true> internal::sub_t<L, R> operator-(L l, R r) { return {std::move(l), std::move(r)}; } template<class L, class R, - typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + - std::is_base_of<internal::arithmetic_t, R>::value > - 0)>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::arithmetic_t, L>, + std::is_base_of<internal::arithmetic_t, R>>, + bool> = true> internal::mul_t<L, R> operator*(L l, R r) { return {std::move(l), std::move(r)}; } template<class L, class R, - typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + - std::is_base_of<internal::arithmetic_t, R>::value > - 0)>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::arithmetic_t, L>, + std::is_base_of<internal::arithmetic_t, R>>, + bool> = true> internal::div_t<L, R> operator/(L l, R r) { return {std::move(l), std::move(r)}; } template<class L, class R, - typename = typename std::enable_if<(std::is_base_of<internal::arithmetic_t, L>::value + - std::is_base_of<internal::arithmetic_t, R>::value > - 0)>::type> + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<internal::arithmetic_t, L>, + std::is_base_of<internal::arithmetic_t, R>>, + bool> = true> internal::mod_t<L, R> operator%(L l, R r) { return {std::move(l), std::move(r)}; } @@ -6119,26 +6460,13 @@ namespace sqlite_orm { namespace internal { - /** - * Cute class used to compare setters/getters and member pointers with each other. - */ template<class L, class R> - struct typed_comparator { - bool operator()(const L&, const R&) const { - return false; - } - }; - + bool compare_any(const L& /*lhs*/, const R& /*rhs*/) { + return false; + } template<class O> - struct typed_comparator<O, O> { - bool operator()(const O& lhs, const O& rhs) const { - return lhs == rhs; - } - }; - - template<class L, class R> - bool compare_any(const L& lhs, const R& rhs) { - return typed_comparator<L, R>()(lhs, rhs); + bool compare_any(const O& lhs, const O& rhs) { + return lhs == rhs; } } } @@ -6147,35 +6475,28 @@ namespace sqlite_orm { #include <string> // std::string #include <utility> // std::declval #include <tuple> // std::tuple, std::get, std::tuple_size -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_optional.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" // #include "is_base_of_template.h" -// #include "tuple_helper/tuple_helper.h" +// #include "tuple_helper/tuple_filter.h" // #include "optional_container.h" // #include "ast/where.h" -// #include "../serialize_result_type.h" +#include <type_traits> // std::false_type, std::true_type +#include <utility> // std::move -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED -#include <string_view> // string_view -#else -#include <string> // std::string -#endif +// #include "../functional/cxx_universal.h" -namespace sqlite_orm { - namespace internal { -#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED - using serialize_result_type = std::string_view; -#else - using serialize_result_type = std::string; -#endif - } -} +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../serialize_result_type.h" namespace sqlite_orm { namespace internal { @@ -6201,10 +6522,10 @@ namespace sqlite_orm { }; template<class T> - struct is_where : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_where_v = polyfill::is_specialization_of_v<T, where_t>; template<class T> - struct is_where<where_t<T>> : std::true_type {}; + using is_where = polyfill::bool_constant<is_where_v<T>>; } /** @@ -6222,6 +6543,83 @@ namespace sqlite_orm { } } +// #include "ast/group_by.h" + +#include <tuple> // std::tuple, std::make_tuple +#include <type_traits> // std::true_type, std::false_type +#include <utility> // std::forward, std::move + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { + + template<class T, class... Args> + struct group_by_with_having { + using args_type = std::tuple<Args...>; + using expression_type = T; + + args_type args; + expression_type expression; + }; + + /** + * GROUP BY pack holder. + */ + template<class... Args> + struct group_by_t { + using args_type = std::tuple<Args...>; + + args_type args; + + template<class T> + group_by_with_having<T, Args...> having(T expression) { + return {move(this->args), std::move(expression)}; + } + }; + + template<class T> + using is_group_by = polyfill::disjunction<polyfill::is_specialization_of<T, group_by_t>, + polyfill::is_specialization_of<T, group_by_with_having>>; + + /** + * HAVING holder. + * T is having argument type. + */ + template<class T> + struct having_t { + using expression_type = T; + + expression_type expression; + }; + + template<class T> + using is_having = polyfill::is_specialization_of<T, having_t>; + } + + /** + * GROUP BY column. + * Example: storage.get_all<Employee>(group_by(&Employee::name)) + */ + template<class... Args> + internal::group_by_t<Args...> group_by(Args&&... args) { + return {std::make_tuple(std::forward<Args>(args)...)}; + } + + /** + * [Deprecation notice]: this function is deprecated and will be removed in v1.9. Please use `group_by(...).having(...)` instead. + * + * HAVING(expression). + * Example: storage.get_all<Employee>(group_by(&Employee::name), having(greater_than(count(&Employee::name), 2))); + */ + template<class T> + [[deprecated("Use group_by(...).having(...) instead")]] internal::having_t<T> having(T expression) { + return {std::move(expression)}; + } +} + +// #include "core_functions.h" + namespace sqlite_orm { namespace internal { @@ -6275,28 +6673,24 @@ namespace sqlite_orm { columns_type columns; bool distinct = false; - static constexpr const int count = std::tuple_size<columns_type>::value; + static constexpr int count = std::tuple_size<columns_type>::value; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + columns_t(columns_type columns) : columns{move(columns)} {} +#endif }; template<class T> - struct is_columns : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_columns_v = polyfill::is_specialization_of_v<T, columns_t>; - template<class... Args> - struct is_columns<columns_t<Args...>> : std::true_type {}; - - struct set_string { - operator std::string() const { - return "SET"; - } - }; + template<class T> + using is_columns = polyfill::bool_constant<is_columns_v<T>>; template<class... Args> - struct set_t : set_string { + struct set_t { using assigns_type = std::tuple<Args...>; assigns_type assigns; - - set_t(assigns_type assigns_) : assigns(move(assigns_)) {} }; /** @@ -6353,13 +6747,17 @@ namespace sqlite_orm { return_type col; conditions_type conditions; bool highest_level = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + select_t(return_type col, conditions_type conditions) : col{std::move(col)}, conditions{move(conditions)} {} +#endif }; template<class T> - struct is_select : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_select_v = polyfill::is_specialization_of_v<T, select_t>; - template<class T, class... Args> - struct is_select<select_t<T, Args...>> : std::true_type {}; + template<class T> + using is_select = polyfill::bool_constant<is_select_v<T>>; /** * Base for UNION, UNION ALL, EXCEPT and INTERSECT @@ -6381,6 +6779,10 @@ namespace sqlite_orm { struct union_base { bool all = false; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + union_base(bool all) : all{all} {} +#endif + operator std::string() const { if(!this->all) { return "UNION"; @@ -6398,10 +6800,10 @@ namespace sqlite_orm { using left_type = typename compound_operator<L, R>::left_type; using right_type = typename compound_operator<L, R>::right_type; - union_t(left_type l, right_type r, decltype(all) all_) : - compound_operator<L, R>(std::move(l), std::move(r)), union_base{all_} {} + union_t(left_type l, right_type r, bool all_) : + compound_operator<L, R>{std::move(l), std::move(r)}, union_base{all_} {} - union_t(left_type l, right_type r) : union_t(std::move(l), std::move(r), false) {} + union_t(left_type l, right_type r) : union_t{std::move(l), std::move(r), false} {} }; struct except_string { @@ -6455,11 +6857,23 @@ namespace sqlite_orm { template<class T> struct asterisk_t { using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + asterisk_t(bool definedOrder) : defined_order{definedOrder} {} +#endif }; template<class T> struct object_t { using type = T; + + bool defined_order = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_t(bool definedOrder) : defined_order{definedOrder} {} +#endif }; template<class T> @@ -6501,8 +6915,7 @@ namespace sqlite_orm { simple_case_builder<R, T, E, Args..., std::pair<W, Th>> when(W w, then_t<Th> t) { using result_args_type = std::tuple<Args..., std::pair<W, Th>>; std::pair<W, Th> newPair{std::move(w), std::move(t.expression)}; - result_args_type result_args = - std::tuple_cat(std::move(this->args), std::move(std::make_tuple(newPair))); + result_args_type result_args = std::tuple_cat(std::move(this->args), std::make_tuple(newPair)); std::get<std::tuple_size<result_args_type>::value - 1>(result_args) = std::move(newPair); return {std::move(this->case_expression), std::move(result_args), std::move(this->else_expression)}; } @@ -6638,33 +7051,51 @@ namespace sqlite_orm { } /** - * SELECT * FROM T function. - * T is typed mapped to a storage. - * Example: auto rows = storage.select(asterisk<User>()); - * // decltype(rows) is std::vector<std::tuple<...all column typed in declared in make_table order...>> - * If you need to fetch result as objects not tuple please use `object<T>` instead. + * `SELECT * FROM T` expression that fetches results as tuples. + * T is a type mapped to a storage, or an alias of it. + * The `definedOrder` parameter denotes the expected order of result columns. + * The default is the implicit order as returned by SQLite, which may differ from the defined order + * if the schema of a table has been changed. + * By specifying the defined order, the columns are written out in the resulting select SQL string. + * + * In pseudo code: + * select(asterisk<User>(false)) -> SELECT * from User + * select(asterisk<User>(true)) -> SELECT id, name from User + * + * Example: auto rows = storage.select(asterisk<User>()); + * // decltype(rows) is std::vector<std::tuple<...all columns in implicitly stored order...>> + * Example: auto rows = storage.select(asterisk<User>(true)); + * // decltype(rows) is std::vector<std::tuple<...all columns in declared make_table order...>> + * + * If you need to fetch results as objects instead of tuples please use `object<T>()`. */ template<class T> - internal::asterisk_t<T> asterisk() { - return {}; + internal::asterisk_t<T> asterisk(bool definedOrder = false) { + return {definedOrder}; } /** - * SELECT * FROM T function. - * T is typed mapped to a storage. - * Example: auto rows = storage.select(object<User>()); - * // decltype(rows) is std::vector<User> - * If you need to fetch result as tuples not objects please use `asterisk<T>` instead. + * `SELECT * FROM T` expression that fetches results as objects of type T. + * T is a type mapped to a storage, or an alias of it. + * + * Example: auto rows = storage.select(object<User>()); + * // decltype(rows) is std::vector<User>, where the User objects are constructed from columns in implicitly stored order + * Example: auto rows = storage.select(object<User>(true)); + * // decltype(rows) is std::vector<User>, where the User objects are constructed from columns in declared make_table order + * + * If you need to fetch results as tuples instead of objects please use `asterisk<T>()`. */ template<class T> - internal::object_t<T> object() { - return {}; + internal::object_t<T> object(bool definedOrder = false) { + return {definedOrder}; } } #pragma once #include <string> // std::string +// #include "functional/cxx_universal.h" + namespace sqlite_orm { struct table_info { @@ -6675,6 +7106,7 @@ namespace sqlite_orm { std::string dflt_value; int pk = 0; +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) table_info(decltype(cid) cid_, decltype(name) name_, decltype(type) type_, @@ -6683,13 +7115,325 @@ namespace sqlite_orm { decltype(pk) pk_) : cid(cid_), name(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(move(dflt_value_)), pk(pk_) {} +#endif }; + struct table_xinfo { + int cid = 0; + std::string name; + std::string type; + bool notnull = false; + std::string dflt_value; + int pk = 0; + int hidden = 0; // different than 0 => generated_always_as() - TODO verify + +#if !defined(SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED) || !defined(SQLITE_ORM_AGGREGATE_PAREN_INIT_SUPPORTED) + table_xinfo(decltype(cid) cid_, + decltype(name) name_, + decltype(type) type_, + decltype(notnull) notnull_, + decltype(dflt_value) dflt_value_, + decltype(pk) pk_, + decltype(hidden) hidden_) : + cid(cid_), + name(move(name_)), type(move(type_)), notnull(notnull_), dflt_value(move(dflt_value_)), + pk(pk_), hidden{hidden_} {} +#endif + }; +} +#pragma once + +#include <memory> +#include <sstream> +#include <string> +#include <tuple> + +// #include "functional/cxx_universal.h" + +// #include "optional_container.h" + +// NOTE Idea : Maybe also implement a custom trigger system to call a c++ callback when a trigger triggers ? +// (Could be implemented with a normal trigger that insert or update an internal table and then retreive +// the event in the C++ code, to call the C++ user callback, with update hooks: https://www.sqlite.org/c3ref/update_hook.html) +// It could be an interesting feature to bring to sqlite_orm that other libraries don't have ? + +namespace sqlite_orm { + namespace internal { + enum class trigger_timing { trigger_before, trigger_after, trigger_instead_of }; + enum class trigger_type { trigger_delete, trigger_insert, trigger_update }; + + /** + * This class is an intermediate SQLite trigger, to be used with + * `make_trigger` to create a full trigger. + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statements + */ + template<class T, class... S> + struct partial_trigger_t { + using statements_type = std::tuple<S...>; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + statements_type statements; + + partial_trigger_t(T trigger_base, S... statements) : + base{std::move(trigger_base)}, statements{std::make_tuple<S...>(std::forward<S>(statements)...)} {} + + partial_trigger_t& end() { + return *this; + } + }; + + struct base_trigger { + /** + * Name of the trigger + */ + std::string name; + }; + + /** + * This class represent a SQLite trigger + * T is the base of the trigger (contains its type, timing and associated table) + * S is the list of trigger statments + */ + template<class T, class... S> + struct trigger_t : base_trigger { + using object_type = void; + using elements_type = typename partial_trigger_t<T, S...>::statements_type; + + /** + * Base of the trigger (contains its type, timing and associated table) + */ + T base; + + /** + * Statements of the triggers (to be executed when the trigger fires) + */ + elements_type elements; + +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + trigger_t(std::string name, T trigger_base, elements_type statements) : + base_trigger{move(name)}, base(std::move(trigger_base)), elements(move(statements)) {} +#endif + }; + + /** + * Base of a trigger. Contains the trigger type/timming and the table type + * T is the table type + * W is `when` expression type + * Type is the trigger base type (type+timing) + */ + template<class T, class W, class Type> + struct trigger_base_t { + using table_type = T; + using when_type = W; + using trigger_type_base = Type; + + /** + * Contains the trigger type and timing + */ + trigger_type_base type_base; + /** + * Value used to determine if we execute the trigger on each row or on each statement + * (SQLite doesn't support the FOR EACH STATEMENT syntax yet: https://sqlite.org/lang_createtrigger.html#description + * so this value is more of a placeholder for a later update) + */ + bool do_for_each_row = false; + /** + * When expression (if any) + * If a WHEN expression is specified, the trigger will only execute + * if the expression evaluates to true when the trigger is fired + */ + optional_container<when_type> container_when; + + trigger_base_t(trigger_type_base type_base_) : type_base(std::move(type_base_)) {} + + trigger_base_t& for_each_row() { + this->do_for_each_row = true; + return *this; + } + + template<class WW> + trigger_base_t<T, WW, Type> when(WW expression) { + trigger_base_t<T, WW, Type> res(this->type_base); + res.container_when.field = std::move(expression); + return res; + } + + template<class... S> + partial_trigger_t<trigger_base_t<T, W, Type>, S...> begin(S... statements) { + return {*this, std::forward<S>(statements)...}; + } + }; + + /** + * Contains the trigger type and timing + */ + struct trigger_type_base_t { + /** + * Value indicating if the trigger is run BEFORE, AFTER or INSTEAD OF + * the statement that fired it. + */ + trigger_timing timing; + /** + * The type of the statement that would cause the trigger to fire. + * Can be DELETE, INSERT, or UPDATE. + */ + trigger_type type; + + trigger_type_base_t(trigger_timing timing, trigger_type type) : timing(timing), type(type) {} + + template<class T> + trigger_base_t<T, void, trigger_type_base_t> on() { + return {*this}; + } + }; + + /** + * Special case for UPDATE OF (columns) + * Contains the trigger type and timing + */ + template<class... Cs> + struct trigger_update_type_t : trigger_type_base_t { + using columns_type = std::tuple<Cs...>; + + /** + * Contains the columns the trigger is watching. Will only + * trigger if one of theses columns is updated. + */ + columns_type columns; + + trigger_update_type_t(trigger_timing timing, trigger_type type, Cs... columns) : + trigger_type_base_t(timing, type), columns(std::make_tuple<Cs...>(std::forward<Cs>(columns)...)) {} + + template<class T> + trigger_base_t<T, void, trigger_update_type_t<Cs...>> on() { + return {*this}; + } + }; + + struct trigger_timing_t { + trigger_timing timing; + + trigger_type_base_t delete_() { + return {timing, trigger_type::trigger_delete}; + } + + trigger_type_base_t insert() { + return {timing, trigger_type::trigger_insert}; + } + + trigger_type_base_t update() { + return {timing, trigger_type::trigger_update}; + } + + template<class... Cs> + trigger_update_type_t<Cs...> update_of(Cs... columns) { + return {timing, trigger_type::trigger_update, std::forward<Cs>(columns)...}; + } + }; + + struct raise_t { + enum class type_t { + ignore, + rollback, + abort, + fail, + }; + + type_t type = type_t::ignore; + std::string message; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + raise_t(type_t type, std::string message) : type{type}, message{move(message)} {} +#endif + }; + + template<class T> + struct new_t { + using expression_type = T; + + expression_type expression; + }; + + template<class T> + struct old_t { + using expression_type = T; + + expression_type expression; + }; + } // NAMESPACE internal + + /** + * NEW.expression function used within TRIGGER expressions + */ + template<class T> + internal::new_t<T> new_(T expression) { + return {std::move(expression)}; + } + + /** + * OLD.expression function used within TRIGGER expressions + */ + template<class T> + internal::old_t<T> old(T expression) { + return {std::move(expression)}; + } + + /** + * RAISE(IGNORE) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_ignore() { + return {internal::raise_t::type_t::ignore, {}}; + } + + /** + * RAISE(ROLLBACK, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_rollback(std::string message) { + return {internal::raise_t::type_t::rollback, move(message)}; + } + + /** + * RAISE(ABORT, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_abort(std::string message) { + return {internal::raise_t::type_t::abort, move(message)}; + } + + /** + * RAISE(FAIL, %message%) expression used within TRIGGER expressions + */ + inline internal::raise_t raise_fail(std::string message) { + return {internal::raise_t::type_t::fail, move(message)}; + } + + template<class T, class... S> + internal::trigger_t<T, S...> make_trigger(std::string name, const internal::partial_trigger_t<T, S...>& part) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {move(name), std::move(part.base), std::move(part.statements)}); + } + + inline internal::trigger_timing_t before() { + return {internal::trigger_timing::trigger_before}; + } + + inline internal::trigger_timing_t after() { + return {internal::trigger_timing::trigger_after}; + } + + inline internal::trigger_timing_t instead_of() { + return {internal::trigger_timing::trigger_instead_of}; + } } #pragma once -#include <memory> // std::unique_ptr #include <sqlite3.h> +#include <memory> // std::unique_ptr #include <type_traits> // std::integral_constant namespace sqlite_orm { @@ -6699,9 +7443,9 @@ namespace sqlite_orm { */ using statement_finalizer = std::unique_ptr<sqlite3_stmt, std::integral_constant<decltype(&sqlite3_finalize), sqlite3_finalize>>; - } #pragma once +#include <type_traits> namespace sqlite_orm { @@ -6713,67 +7457,590 @@ namespace sqlite_orm { struct real_tag {}; template<class V> - struct arithmetic_tag { - using type = std::conditional_t<std::is_integral<V>::value, - // Integer class - std::conditional_t<sizeof(V) <= sizeof(int), int_or_smaller_tag, bigint_tag>, - // Floating-point class - real_tag>; + using arithmetic_tag_t = + std::conditional_t<std::is_integral<V>::value, + // Integer class + std::conditional_t<sizeof(V) <= sizeof(int), int_or_smaller_tag, bigint_tag>, + // Floating-point class + real_tag>; +} +#pragma once + +#include <type_traits> +#include <memory> +#include <utility> + +// #include "functional/cxx_universal.h" + +// #include "xdestroy_handling.h" + +#include <type_traits> // std::integral_constant +#if defined(SQLITE_ORM_CONCEPTS_SUPPORTED) && SQLITE_ORM_HAS_INCLUDE(<concepts>) +#include <concepts> +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + + using xdestroy_fn_t = void (*)(void*); + using null_xdestroy_t = std::integral_constant<xdestroy_fn_t, nullptr>; + SQLITE_ORM_INLINE_VAR constexpr null_xdestroy_t null_xdestroy_f{}; +} + +namespace sqlite_orm { + namespace internal { +#ifdef SQLITE_ORM_CONCEPTS_SUPPORTED + /** + * Constrains a deleter to be state-less. + */ + template<typename D> + concept stateless_deleter = std::is_empty_v<D> && std::is_default_constructible_v<D>; + + /** + * Constrains a deleter to be an integral function constant. + */ + template<typename D> + concept integral_fp_c = requires { + typename D::value_type; + D::value; + requires std::is_function_v<std::remove_pointer_t<typename D::value_type>>; + }; + + /** + * Constrains a deleter to be or to yield a function pointer. + */ + template<typename D> + concept yields_fp = requires(D d) { + // yielding function pointer by using the plus trick + { +d }; + requires std::is_function_v<std::remove_pointer_t<decltype(+d)>>; + }; +#endif + +#if __cpp_lib_concepts >= 201907L + /** + * Yield a deleter's function pointer. + */ + template<yields_fp D> + struct yield_fp_of { + using type = decltype(+std::declval<D>()); + }; +#else + + template<typename D> + SQLITE_ORM_INLINE_VAR constexpr bool is_stateless_deleter_v = + std::is_empty<D>::value && std::is_default_constructible<D>::value; + + template<typename D, typename SFINAE = void> + struct is_integral_fp_c : std::false_type {}; + template<typename D> + struct is_integral_fp_c< + D, + polyfill::void_t<typename D::value_type, + decltype(D::value), + std::enable_if_t<std::is_function<std::remove_pointer_t<typename D::value_type>>::value>>> + : std::true_type {}; + template<typename D> + SQLITE_ORM_INLINE_VAR constexpr bool is_integral_fp_c_v = is_integral_fp_c<D>::value; + + template<typename D, typename SFINAE = void> + struct can_yield_fp : std::false_type {}; + template<typename D> + struct can_yield_fp< + D, + polyfill::void_t< + decltype(+std::declval<D>()), + std::enable_if_t<std::is_function<std::remove_pointer_t<decltype(+std::declval<D>())>>::value>>> + : std::true_type {}; + template<typename D> + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_fp_v = can_yield_fp<D>::value; + + template<typename D, bool = can_yield_fp_v<D>> + struct yield_fp_of { + using type = void; + }; + template<typename D> + struct yield_fp_of<D, true> { + using type = decltype(+std::declval<D>()); + }; +#endif + template<typename D> + using yielded_fn_t = typename yield_fp_of<D>::type; + +#if __cpp_lib_concepts >= 201907L + template<typename D> + concept is_unusable_for_xdestroy = (!stateless_deleter<D> && + (yields_fp<D> && !std::convertible_to<yielded_fn_t<D>, xdestroy_fn_t>)); + + /** + * This concept tests whether a deleter yields a function pointer, which is convertible to an xdestroy function pointer. + * Note: We are using 'is convertible' rather than 'is same' because of any exception specification. + */ + template<typename D> + concept yields_xdestroy = yields_fp<D> && std::convertible_to<yielded_fn_t<D>, xdestroy_fn_t>; + + template<typename D, typename P> + concept needs_xdestroy_proxy = (stateless_deleter<D> && + (!yields_fp<D> || !std::convertible_to<yielded_fn_t<D>, xdestroy_fn_t>)); + + /** + * xDestroy function that constructs and invokes the stateless deleter. + * + * Requires that the deleter can be called with the q-qualified pointer argument; + * it doesn't check so explicitly, but a compiler error will occur. + */ + template<typename D, typename P> + requires(!integral_fp_c<D>) + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder<pointer_binding<P, T, D>> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + /** + * xDestroy function that invokes the integral function pointer constant. + * + * Performs a const-cast of the argument pointer in order to allow for C API functions + * that take a non-const parameter, but user code passes a pointer to a const object. + */ + template<integral_fp_c D, typename P> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder<pointer_binding<P, T, D>>, + auto o = (std::remove_cv_t<P>*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#else + template<typename D> + SQLITE_ORM_INLINE_VAR constexpr bool is_unusable_for_xdestroy_v = + !is_stateless_deleter_v<D> && + (can_yield_fp_v<D> && !std::is_convertible<yielded_fn_t<D>, xdestroy_fn_t>::value); + + template<typename D> + SQLITE_ORM_INLINE_VAR constexpr bool can_yield_xdestroy_v = + can_yield_fp_v<D> && std::is_convertible<yielded_fn_t<D>, xdestroy_fn_t>::value; + + template<typename D, typename P> + SQLITE_ORM_INLINE_VAR constexpr bool needs_xdestroy_proxy_v = + is_stateless_deleter_v<D> && + (!can_yield_fp_v<D> || !std::is_convertible<yielded_fn_t<D>, xdestroy_fn_t>::value); + + template<typename D, typename P, std::enable_if_t<!is_integral_fp_c_v<D>, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder<pointer_binding<P, T, D>> + auto o = (P*)p; + // ignoring return code + (void)D{}(o); + } + + template<typename D, typename P, std::enable_if_t<is_integral_fp_c_v<D>, bool> = true> + void xdestroy_proxy(void* p) noexcept { + // C-casting `void* -> P*` like statement_binder<pointer_binding<P, T, D>>, + auto o = (std::remove_cv_t<P>*)(P*)p; + // ignoring return code + (void)D{}(o); + } +#endif + } +} + +namespace sqlite_orm { + +#if __cpp_lib_concepts >= 201907L + /** + * Prohibits using a yielded function pointer, which is not of type xdestroy_fn_t. + * + * Explicitly declared for better error messages. + */ + template<typename D, typename P> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::is_unusable_for_xdestroy<D>) + { + static_assert(polyfill::always_false_v<D>, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + /** + * Obtains a proxy 'xDestroy' function pointer [of type void(*)(void*)] + * for a deleter in a type-safe way. + * + * The deleter can be one of: + * - integral function constant + * - state-less (empty) deleter + * - non-capturing lambda + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template<typename D, typename P> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + requires(internal::needs_xdestroy_proxy<D, P>) + { + return internal::xdestroy_proxy<D, P>; + } + + /** + * Directly obtains a 'xDestroy' function pointer [of type void(*)(void*)] + * from a deleter in a type-safe way. + * + * The deleter can be one of: + * - function pointer of type xdestroy_fn_t + * - structure holding a function pointer + * - integral function constant + * - non-capturing lambda + * ... and yield a function pointer of type xdestroy_fn_t. + * + * Type-safety is garanteed by checking whether the deleter or yielded function pointer + * is invocable with the non-q-qualified pointer value. + */ + template<typename D, typename P> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept + requires(internal::yields_xdestroy<D>) + { + return d; + } +#else + template<typename D, typename P, std::enable_if_t<internal::is_unusable_for_xdestroy_v<D>, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) { + static_assert(polyfill::always_false_v<D>, + "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); + return nullptr; + } + + template<typename D, typename P, std::enable_if_t<internal::needs_xdestroy_proxy_v<D, P>, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept { + return internal::xdestroy_proxy<D, P>; + } + + template<typename D, typename P, std::enable_if_t<internal::can_yield_xdestroy_v<D>, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept { + return d; + } +#endif +} + +namespace sqlite_orm { + + /** + * Wraps a pointer and tags it with a pointer type, + * used for accepting function parameters, + * facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - P: The value type, possibly const-qualified. + * - T: An integral constant string denoting the pointer type, e.g. `carray_pvt_name`. + * + */ + template<typename P, typename T> + struct pointer_arg { + + static_assert(std::is_convertible<typename T::value_type, const char*>::value, + "`std::integral_constant<>` must be convertible to `const char*`"); + + using tag = T; + P* p_; + + P* ptr() const noexcept { + return p_; + } + + operator P*() const noexcept { + return p_; + } }; - template<class V> - using arithmetic_tag_t = typename arithmetic_tag<V>::type; + /** + * Pointer value with associated deleter function, + * used for returning or binding pointer values + * as part of facilitating the 'pointer-passing interface'. + * + * Template parameters: + * - D: The deleter for the pointer value; + * can be one of: + * - function pointer + * - integral function pointer constant + * - state-less (empty) deleter + * - non-capturing lambda + * - structure implicitly yielding a function pointer + * + * @note Use one of the factory functions to create a pointer binding, + * e.g. bindable_carray_pointer or statically_bindable_carray_pointer(). + * + * @example + * ``` + * int64 rememberedId; + * storage.select(func<remember_fn>(&Object::id, statically_bindable_carray_pointer(&rememberedId))); + * ``` + */ + template<typename P, typename T, typename D> + class pointer_binding { + + P* p_; + SQLITE_ORM_NOUNIQUEADDRESS + D d_; + + protected: + // Constructing pointer bindings must go through bindable_pointer() + template<class T2, class P2, class D2> + friend auto bindable_pointer(P2*, D2) noexcept -> pointer_binding<P2, T2, D2>; + template<class B> + friend B bindable_pointer(typename B::qualified_type*, typename B::deleter_type) noexcept; + + // Construct from pointer and deleter. + // Transfers ownership of the passed in object. + pointer_binding(P* p, D d = {}) noexcept : p_{p}, d_{std::move(d)} {} + + public: + using qualified_type = P; + using tag = T; + using deleter_type = D; + + pointer_binding(const pointer_binding&) = delete; + pointer_binding& operator=(const pointer_binding&) = delete; + pointer_binding& operator=(pointer_binding&&) = delete; + + pointer_binding(pointer_binding&& other) noexcept : + p_{std::exchange(other.p_, nullptr)}, d_{std::move(other.d_)} {} + + ~pointer_binding() { + if(p_) { + if(auto xDestroy = get_xdestroy()) { + // note: C-casting `P* -> void*` like statement_binder<pointer_binding<P, T, D>> + xDestroy((void*)p_); + } + } + } + + P* ptr() const noexcept { + return p_; + } + + P* take_ptr() noexcept { + return std::exchange(p_, nullptr); + } + + xdestroy_fn_t get_xdestroy() const noexcept { + return obtain_xdestroy_for(d_, p_); + } + }; + + /** + * Template alias for a static pointer value binding. + * 'Static' means that ownership won't be transferred to sqlite, + * sqlite doesn't delete it, and sqlite assumes the object + * pointed to is valid throughout the lifetime of a statement. + */ + template<typename P, typename T> + using static_pointer_binding = pointer_binding<P, T, null_xdestroy_t>; +} + +namespace sqlite_orm { + + /** + * Wrap a pointer, its type and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template<class T, class P, class D> + auto bindable_pointer(P* p, D d) noexcept -> pointer_binding<P, T, D> { + return {p, std::move(d)}; + } + + template<class T, class P, class D> + auto bindable_pointer(std::unique_ptr<P, D> p) noexcept -> pointer_binding<P, T, D> { + return bindable_pointer<T>(p.release(), p.get_deleter()); + } + + template<typename B> + B bindable_pointer(typename B::qualified_type* p, typename B::deleter_type d = {}) noexcept { + return B{p, std::move(d)}; + } + + /** + * Wrap a pointer and its type for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template<class T, class P> + auto statically_bindable_pointer(P* p) noexcept -> static_pointer_binding<P, T> { + return bindable_pointer<T>(p, null_xdestroy_f); + } + + template<typename B> + B statically_bindable_pointer(typename B::qualified_type* p, + typename B::deleter_type* /*exposition*/ = nullptr) noexcept { + return bindable_pointer<B>(p); + } + + /** + * Forward a pointer value from an argument. + */ + template<class P, class T> + auto rebind_statically(const pointer_arg<P, T>& pv) noexcept -> static_pointer_binding<P, T> { + return statically_bindable_pointer<T>(pv.ptr()); + } } #pragma once #include <sqlite3.h> -#include <type_traits> // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type +#include <type_traits> // std::enable_if_t, std::is_arithmetic, std::is_same, std::true_type, std::false_type, std::make_index_sequence, std::index_sequence +#include <memory> // std::default_delete #include <string> // std::string, std::wstring -#ifndef SQLITE_ORM_OMITS_CODECVT -#include <codecvt> // std::codecvt_utf8_utf16 -#endif // SQLITE_ORM_OMITS_CODECVT #include <vector> // std::vector -#include <cstddef> // std::nullptr_t -#include <utility> // std::declval -#include <locale> // std::wstring_convert #include <cstring> // ::strncpy, ::strlen +// #include "functional/cxx_string_view.h" -// #include "is_std_ptr.h" +#ifndef SQLITE_ORM_STRING_VIEW_SUPPORTED +#include <cwchar> // ::wcsncpy, ::wcslen +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +#include <functional> +#if __cpp_lib_invoke < 201411L +#include <type_traits> // std::enable_if, std::is_member_object_pointer, std::is_member_function_pointer +#endif +#include <utility> // std::forward + +#if __cpp_lib_invoke < 201411L +// #include "cxx_type_traits_polyfill.h" + +#endif +// #include "../member_traits/member_traits.h" namespace sqlite_orm { + namespace internal { + namespace polyfill { + // C++20 or later (unfortunately there's no feature test macro). + // Stupidly, clang < 11 says C++20, but comes w/o std::identity. + // Another way of detection would be the constrained algorithms feature macro __cpp_lib_ranges +#if(__cplusplus >= 202002L) && (!__clang_major__ || __clang_major__ >= 11) + using std::identity; +#else + struct identity { + template<class T> + constexpr T&& operator()(T&& v) const noexcept { + return std::forward<T>(v); + } - /** - * Specialization for optional type (std::shared_ptr / std::unique_ptr). - */ - template<typename T> - struct is_std_ptr : std::false_type {}; + using is_transparent = int; + }; +#endif - template<typename T> - struct is_std_ptr<std::shared_ptr<T>> : std::true_type { - using element_type = T; +#if __cpp_lib_invoke >= 201411L + using std::invoke; +#else + // pointer-to-data-member+object + template<class Callable, + class Object, + class... Args, + class Unqualified = remove_cvref_t<Callable>, + std::enable_if_t<std::is_member_object_pointer<Unqualified>::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return std::forward<Object>(object).*callable; + } - static std::shared_ptr<T> make(const T& v) { - return std::make_shared<T>(v); - } - }; + // pointer-to-member-function+object + template<class Callable, + class Object, + class... Args, + class Unqualified = remove_cvref_t<Callable>, + std::enable_if_t<std::is_member_function_pointer<Unqualified>::value, bool> = true> + decltype(auto) invoke(Callable&& callable, Object&& object, Args&&... args) { + return (std::forward<Object>(object).*callable)(std::forward<Args>(args)...); + } - template<typename T> - struct is_std_ptr<std::unique_ptr<T>> : std::true_type { - using element_type = T; + // pointer-to-member+reference-wrapped object (expect `reference_wrapper::*`) + template<class Callable, + class Object, + class... Args, + std::enable_if_t<polyfill::negation_v<polyfill::is_specialization_of< + member_object_type_t<std::remove_reference_t<Callable>>, + std::reference_wrapper>>, + bool> = true> + decltype(auto) invoke(Callable&& callable, std::reference_wrapper<Object> wrapper, Args&&... args) { + return invoke(std::forward<Callable>(callable), wrapper.get(), std::forward<Args>(args)...); + } - static std::unique_ptr<T> make(const T& v) { - return std::make_unique<T>(v); + // functor + template<class Callable, class... Args> + decltype(auto) invoke(Callable&& callable, Args&&... args) { + return std::forward<Callable>(callable)(std::forward<Args>(args)...); + } +#endif } - }; + } + + namespace polyfill = internal::polyfill; } +// #include "is_std_ptr.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "error_code.h" + +// #include "arithmetic_tag.h" + +// #include "xdestroy_handling.h" + +// #include "pointer_value.h" + namespace sqlite_orm { /** * Helper class used for binding fields to sqlite3 statements. */ template<class V, typename Enable = void> - struct statement_binder : std::false_type {}; + struct statement_binder; + + namespace internal { + + template<class T, class SFINAE = void> + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v = false; + template<class T> + SQLITE_ORM_INLINE_VAR constexpr bool is_bindable_v<T, polyfill::void_t<decltype(statement_binder<T>())>> = true + // note : msvc 14.0 needs the parentheses constructor, otherwise `is_bindable<const char*>` isn't recognised. + // The strangest thing is that this is mutually exclusive with `is_printable_v`. + ; + + template<class T> + using is_bindable = polyfill::bool_constant<is_bindable_v<T>>; + + } + + /** + * Specialization for 'pointer-passing interface'. + */ + template<class P, class T, class D> + struct statement_binder<pointer_binding<P, T, D>, void> { + using V = pointer_binding<P, T, D>; + + // ownership of pointed-to-object is left untouched and remains at prepared statement's AST expression + int bind(sqlite3_stmt* stmt, int index, const V& value) const { + // note: C-casting `P* -> void*`, internal::xdestroy_proxy() does the inverse + return sqlite3_bind_pointer(stmt, index, (void*)value.ptr(), T::value, null_xdestroy_f); + } + + // ownership of pointed-to-object is transferred to sqlite + void result(sqlite3_context* context, V& value) const { + // note: C-casting `P* -> void*`, + // row_extractor<pointer_arg<P, T>>::extract() and internal::xdestroy_proxy() do the inverse + sqlite3_result_pointer(context, (void*)value.take_ptr(), T::value, value.get_xdestroy()); + } + }; /** * Specialization for arithmetic types. @@ -6792,27 +8059,27 @@ namespace sqlite_orm { private: using tag = arithmetic_tag_t<V>; - int bind(sqlite3_stmt* stmt, int index, const V& value, const int_or_smaller_tag&) const { + int bind(sqlite3_stmt* stmt, int index, const V& value, int_or_smaller_tag) const { return sqlite3_bind_int(stmt, index, static_cast<int>(value)); } - void result(sqlite3_context* context, const V& value, const int_or_smaller_tag&) const { + void result(sqlite3_context* context, const V& value, int_or_smaller_tag) const { sqlite3_result_int(context, static_cast<int>(value)); } - int bind(sqlite3_stmt* stmt, int index, const V& value, const bigint_tag&) const { + int bind(sqlite3_stmt* stmt, int index, const V& value, bigint_tag) const { return sqlite3_bind_int64(stmt, index, static_cast<sqlite3_int64>(value)); } - void result(sqlite3_context* context, const V& value, const bigint_tag&) const { + void result(sqlite3_context* context, const V& value, bigint_tag) const { sqlite3_result_int64(context, static_cast<sqlite3_int64>(value)); } - int bind(sqlite3_stmt* stmt, int index, const V& value, const real_tag&) const { + int bind(sqlite3_stmt* stmt, int index, const V& value, real_tag) const { return sqlite3_bind_double(stmt, index, static_cast<double>(value)); } - void result(sqlite3_context* context, const V& value, const real_tag&) const { + void result(sqlite3_context* context, const V& value, real_tag) const { sqlite3_result_double(context, static_cast<double>(value)); } }; @@ -6821,69 +8088,94 @@ namespace sqlite_orm { * Specialization for std::string and C-string. */ template<class V> - struct statement_binder< - V, - std::enable_if_t<std::is_same<V, std::string>::value || std::is_same<V, const char*>::value>> { + struct statement_binder<V, + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<std::string, V>, + std::is_same<V, const char*> +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same<V, std::string_view> +#endif + >>> { int bind(sqlite3_stmt* stmt, int index, const V& value) const { auto stringData = this->string_data(value); - return sqlite3_bind_text(stmt, index, std::get<0>(stringData), std::get<1>(stringData), SQLITE_TRANSIENT); + return sqlite3_bind_text(stmt, index, stringData.first, stringData.second, SQLITE_TRANSIENT); } void result(sqlite3_context* context, const V& value) const { auto stringData = this->string_data(value); - auto stringDataLength = std::get<1>(stringData); - auto dataCopy = new char[stringDataLength + 1]; - auto stringChars = std::get<0>(stringData); - ::strncpy(dataCopy, stringChars, stringDataLength + 1); - sqlite3_result_text(context, dataCopy, stringDataLength, [](void* pointer) { - auto charPointer = (char*)pointer; - delete[] charPointer; - }); + auto dataCopy = new char[stringData.second + 1]; + constexpr auto deleter = std::default_delete<char[]>{}; + ::strncpy(dataCopy, stringData.first, stringData.second + 1); + sqlite3_result_text(context, dataCopy, stringData.second, obtain_xdestroy_for(deleter, dataCopy)); } private: - std::tuple<const char*, int> string_data(const std::string& s) const { +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair<const char*, int> string_data(const std::string_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair<const char*, int> string_data(const std::string& s) const { return {s.c_str(), int(s.size())}; } - std::tuple<const char*, int> string_data(const char* s) const { - auto length = int(::strlen(s)); - return {s, length}; + std::pair<const char*, int> string_data(const char* s) const { + return {s, int(::strlen(s))}; } +#endif }; #ifndef SQLITE_ORM_OMITS_CODECVT - /** - * Specialization for std::wstring and C-wstring. - */ template<class V> - struct statement_binder< - V, - std::enable_if_t<std::is_same<V, std::wstring>::value || std::is_same<V, const wchar_t*>::value>> { + struct statement_binder<V, + std::enable_if_t<polyfill::disjunction_v<std::is_base_of<std::wstring, V>, + std::is_same<V, const wchar_t*> +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + , + std::is_same<V, std::wstring_view> +#endif + >>> { int bind(sqlite3_stmt* stmt, int index, const V& value) const { + auto stringData = this->string_data(value); std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; - std::string utf8Str = converter.to_bytes(value); + std::string utf8Str = converter.to_bytes(stringData.first, stringData.first + stringData.second); return statement_binder<decltype(utf8Str)>().bind(stmt, index, utf8Str); } void result(sqlite3_context* context, const V& value) const { - sqlite3_result_text16(context, (const void*)value.data(), int(value.length()), nullptr); + auto stringData = this->string_data(value); + sqlite3_result_text16(context, stringData.first, stringData.second, nullptr); } + + private: +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::pair<const wchar_t*, int> string_data(const std::wstring_view& s) const { + return {s.data(), int(s.size())}; + } +#else + std::pair<const wchar_t*, int> string_data(const std::wstring& s) const { + return {s.c_str(), int(s.size())}; + } + + std::pair<const wchar_t*, int> string_data(const wchar_t* s) const { + return {s, int(::wcslen(s))}; + } +#endif }; -#endif // SQLITE_ORM_OMITS_CODECVT +#endif /** - * Specialization for std::nullptr_t. + * Specialization for nullptr_t. */ template<> - struct statement_binder<std::nullptr_t, void> { - int bind(sqlite3_stmt* stmt, int index, const std::nullptr_t&) const { + struct statement_binder<nullptr_t, void> { + int bind(sqlite3_stmt* stmt, int index, const nullptr_t&) const { return sqlite3_bind_null(stmt, index); } - void result(sqlite3_context* context, const std::nullptr_t&) const { + void result(sqlite3_context* context, const nullptr_t&) const { sqlite3_result_null(context); } }; @@ -6905,28 +8197,22 @@ namespace sqlite_orm { #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class V> - struct statement_binder<V, std::enable_if_t<is_std_ptr<V>::value>> { - using value_type = typename is_std_ptr<V>::element_type; + struct statement_binder< + V, + std::enable_if_t<is_std_ptr<V>::value && internal::is_bindable_v<std::remove_cv_t<typename V::element_type>>>> { + using unqualified_type = std::remove_cv_t<typename V::element_type>; int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { - return statement_binder<value_type>().bind(stmt, index, *value); + return statement_binder<unqualified_type>().bind(stmt, index, *value); } else { - return statement_binder<std::nullptr_t>().bind(stmt, index, nullptr); - } - } - - void result(sqlite3_context* context, const V& value) const { - if(value) { - statement_binder<value_type>().result(context, value); - } else { - statement_binder<std::nullptr_t>().result(context, nullptr); + return statement_binder<nullptr_t>().bind(stmt, index, nullptr); } } }; /** - * Specialization for optional type (std::vector<char>). + * Specialization for binary data (std::vector<char>). */ template<> struct statement_binder<std::vector<char>, void> { @@ -6948,93 +8234,106 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<class T> - struct statement_binder<std::optional<T>, void> { - using value_type = T; + template<class V> + struct statement_binder<V, + std::enable_if_t<polyfill::is_specialization_of_v<V, std::optional> && + internal::is_bindable_v<std::remove_cv_t<typename V::value_type>>>> { + using unqualified_type = std::remove_cv_t<typename V::value_type>; - int bind(sqlite3_stmt* stmt, int index, const std::optional<T>& value) const { + int bind(sqlite3_stmt* stmt, int index, const V& value) const { if(value) { - return statement_binder<value_type>().bind(stmt, index, *value); + return statement_binder<unqualified_type>().bind(stmt, index, *value); } else { return statement_binder<std::nullopt_t>().bind(stmt, index, std::nullopt); } } - - void result(sqlite3_context* context, const std::optional<T>& value) const { - if(value) { - statement_binder<value_type>().result(context, value); - } else { - statement_binder<std::nullopt_t>().result(context, std::nullopt); - } - } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED namespace internal { - template<class T> - using is_bindable = std::integral_constant<bool, !std::is_base_of<std::false_type, statement_binder<T>>::value>; - - struct conditional_binder_base { + struct conditional_binder { sqlite3_stmt* stmt = nullptr; - int& index; + int index = 1; - conditional_binder_base(decltype(stmt) stmt_, decltype(index) index_) : stmt(stmt_), index(index_) {} - }; - - template<class T, class C> - struct conditional_binder; + explicit conditional_binder(sqlite3_stmt* stmt) : stmt{stmt} {} - template<class T> - struct conditional_binder<T, std::true_type> : conditional_binder_base { - - using conditional_binder_base::conditional_binder_base; - - int operator()(const T& t) const { - return statement_binder<T>().bind(this->stmt, this->index++, t); + template<class T, satisfies<is_bindable, T> = true> + void operator()(const T& t) { + int rc = statement_binder<T>{}.bind(this->stmt, this->index++, t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(stmt); + } } + + template<class T, satisfies_not<is_bindable, T> = true> + void operator()(const T&) const {} }; - template<class T> - struct conditional_binder<T, std::false_type> : conditional_binder_base { - using conditional_binder_base::conditional_binder_base; + struct field_value_binder : conditional_binder { + using conditional_binder::conditional_binder; + using conditional_binder::operator(); + + template<class T, satisfies_not<is_bindable, T> = true> + void operator()(const T&) const = delete; - int operator()(const T&) const { - return SQLITE_OK; + template<class T> + void operator()(const T* value) { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value); } }; - template<class T, template<class C> class F, class SFINAE = void> - struct tuple_filter_single; - - template<class T, template<class C> class F> - struct tuple_filter_single<T, F, typename std::enable_if<F<T>::value>::type> { - using type = std::tuple<T>; - }; + struct tuple_value_binder { + sqlite3_stmt* stmt = nullptr; - template<class T, template<class C> class F> - struct tuple_filter_single<T, F, typename std::enable_if<!F<T>::value>::type> { - using type = std::tuple<>; - }; + explicit tuple_value_binder(sqlite3_stmt* stmt) : stmt{stmt} {} - template<class T, template<class C> class F> - struct tuple_filter; + template<class Tpl, class Projection> + void operator()(const Tpl& tpl, Projection project) const { + (*this)(tpl, + std::make_index_sequence<std::tuple_size<Tpl>::value>{}, + std::forward<Projection>(project)); + } - template<class... Args, template<class C> class F> - struct tuple_filter<std::tuple<Args...>, F> { - using type = typename conc_tuple<typename tuple_filter_single<Args, F>::type...>::type; - }; + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template<class Tpl, size_t... Idx, class Projection> + void operator()(const Tpl& tpl, std::index_sequence<Idx...>, Projection project) const { + (this->bind(polyfill::invoke(project, std::get<Idx>(tpl)), Idx), ...); + } +#else + template<class Tpl, size_t I, size_t... Idx, class Projection> + void operator()(const Tpl& tpl, std::index_sequence<I, Idx...>, Projection project) const { + this->bind(polyfill::invoke(project, std::get<I>(tpl)), I); + (*this)(tpl, std::index_sequence<Idx...>{}, std::forward<Projection>(project)); + } - template<class T> - struct bindable_filter_single : tuple_filter_single<T, is_bindable> {}; + template<class Tpl, class Projection> + void operator()(const Tpl&, std::index_sequence<>, Projection) const {} +#endif - template<class T> - struct bindable_filter; + template<class T> + void bind(const T& t, size_t idx) const { + int rc = statement_binder<T>{}.bind(this->stmt, int(idx + 1), t); + if(SQLITE_OK != rc) { + throw_translated_sqlite_error(stmt); + } + } - template<class... Args> - struct bindable_filter<std::tuple<Args...>> { - using type = typename conc_tuple<typename bindable_filter_single<Args>::type...>::type; + template<class T> + void bind(const T* value, size_t idx) const { + if(!value) { + throw std::system_error{orm_error_code::value_is_null}; + } + (*this)(*value, idx); + } }; + + template<class Tpl> + using bindable_filter_t = filter_tuple_t<Tpl, is_bindable>; } } #pragma once @@ -7042,36 +8341,50 @@ namespace sqlite_orm { #include <sqlite3.h> #include <type_traits> // std::enable_if_t, std::is_arithmetic, std::is_same, std::enable_if #include <stdlib.h> // atof, atoi, atoll +#include <system_error> // std::system_error #include <string> // std::string, std::wstring #ifndef SQLITE_ORM_OMITS_CODECVT #include <codecvt> // std::wstring_convert, std::codecvt_utf8_utf16 #endif // SQLITE_ORM_OMITS_CODECVT #include <vector> // std::vector #include <cstring> // strlen +#include <locale> #include <algorithm> // std::copy #include <iterator> // std::back_inserter #include <tuple> // std::tuple, std::tuple_size, std::tuple_element +// #include "functional/cxx_universal.h" + // #include "arithmetic_tag.h" +// #include "pointer_value.h" + // #include "journal_mode.h" +#include <iterator> // std::back_inserter #include <string> // std::string #include <memory> // std::unique_ptr #include <array> // std::array #include <algorithm> // std::transform #include <cctype> // std::toupper -namespace sqlite_orm { - -/** - * Caps case cause of 1) delete keyword; 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling - */ -#ifdef DELETE +#if defined(_WINNT_) +// DELETE is a macro defined in the Windows SDK (winnt.h) +#pragma push_macro("DELETE") #undef DELETE #endif + +namespace sqlite_orm { + + /** + * Caps case because of: + * 1) delete keyword; + * 2) https://www.sqlite.org/pragma.html#pragma_journal_mode original spelling + */ enum class journal_mode : signed char { DELETE = 0, + // An alternate enumeration value when using the Windows SDK that defines DELETE as a macro. + DELETE_ = DELETE, TRUNCATE = 1, PERSIST = 2, MEMORY = 3, @@ -7116,8 +8429,14 @@ namespace sqlite_orm { } } +#if defined(_WINNT_) +#pragma pop_macro("DELETE") +#endif + // #include "error_code.h" +// #include "is_std_ptr.h" + namespace sqlite_orm { /** @@ -7128,15 +8447,45 @@ namespace sqlite_orm { template<class V, typename Enable = void> struct row_extractor { // used in sqlite3_exec (select) - V extract(const char* row_value) const; + V extract(const char* row_value) const = delete; // used in sqlite_column (iteration, get_all) - V extract(sqlite3_stmt* stmt, int columnIndex) const; + V extract(sqlite3_stmt* stmt, int columnIndex) const = delete; // used in user defined functions - V extract(sqlite3_value* value) const; + V extract(sqlite3_value* value) const = delete; }; + template<class R> + int extract_single_value(void* data, int argc, char** argv, char**) { + auto& res = *(R*)data; + if(argc) { + res = row_extractor<R>{}.extract(argv[0]); + } + return 0; + } + + /** + * Specialization for the 'pointer-passing interface'. + * + * @note The 'pointer-passing' interface doesn't support (and in fact prohibits) + * extracting pointers from columns. + */ + template<class P, class T> + struct row_extractor<pointer_arg<P, T>, void> { + using V = pointer_arg<P, T>; + + V extract(sqlite3_value* value) const { + return {(P*)sqlite3_value_pointer(value, T::value)}; + } + }; + + /** + * Undefine using pointer_binding<> for querying values + */ + template<class P, class T, class D> + struct row_extractor<pointer_binding<P, T, D>, void>; + /** * Specialization for arithmetic types. */ @@ -7260,11 +8609,11 @@ namespace sqlite_orm { template<class V> struct row_extractor<V, std::enable_if_t<is_std_ptr<V>::value>> { - using value_type = typename is_std_ptr<V>::element_type; + using unqualified_type = std::remove_cv_t<typename V::element_type>; V extract(const char* row_value) const { if(row_value) { - return is_std_ptr<V>::make(row_extractor<value_type>().extract(row_value)); + return is_std_ptr<V>::make(row_extractor<unqualified_type>().extract(row_value)); } else { return {}; } @@ -7273,7 +8622,7 @@ namespace sqlite_orm { V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { - return is_std_ptr<V>::make(row_extractor<value_type>().extract(stmt, columnIndex)); + return is_std_ptr<V>::make(row_extractor<unqualified_type>().extract(stmt, columnIndex)); } else { return {}; } @@ -7282,7 +8631,7 @@ namespace sqlite_orm { V extract(sqlite3_value* value) const { auto type = sqlite3_value_type(value); if(type != SQLITE_NULL) { - return is_std_ptr<V>::make(row_extractor<value_type>().extract(value)); + return is_std_ptr<V>::make(row_extractor<unqualified_type>().extract(value)); } else { return {}; } @@ -7290,73 +8639,71 @@ namespace sqlite_orm { }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<class T> - struct row_extractor<std::optional<T>, void> { - using value_type = T; + template<class V> + struct row_extractor<V, std::enable_if_t<polyfill::is_specialization_of_v<V, std::optional>>> { + using unqualified_type = std::remove_cv_t<typename V::value_type>; - std::optional<T> extract(const char* row_value) const { + V extract(const char* row_value) const { if(row_value) { - return std::make_optional(row_extractor<value_type>().extract(row_value)); + return std::make_optional(row_extractor<unqualified_type>().extract(row_value)); } else { return std::nullopt; } } - std::optional<T> extract(sqlite3_stmt* stmt, int columnIndex) const { + V extract(sqlite3_stmt* stmt, int columnIndex) const { auto type = sqlite3_column_type(stmt, columnIndex); if(type != SQLITE_NULL) { - return std::make_optional(row_extractor<value_type>().extract(stmt, columnIndex)); + return std::make_optional(row_extractor<unqualified_type>().extract(stmt, columnIndex)); } else { return std::nullopt; } } - std::optional<T> extract(sqlite3_value* value) const { + V extract(sqlite3_value* value) const { auto type = sqlite3_value_type(value); if(type != SQLITE_NULL) { - return std::make_optional(row_extractor<value_type>().extract(value)); + return std::make_optional(row_extractor<unqualified_type>().extract(value)); } else { return std::nullopt; } } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED + + template<> + struct row_extractor<nullptr_t> { + nullptr_t extract(const char* /*row_value*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_stmt*, int /*columnIndex*/) const { + return nullptr; + } + + nullptr_t extract(sqlite3_value*) const { + return nullptr; + } + }; /** * Specialization for std::vector<char>. */ template<> struct row_extractor<std::vector<char>> { std::vector<char> extract(const char* row_value) const { - if(row_value) { - auto len = ::strlen(row_value); - return this->go(row_value, len); - } else { - return {}; - } + return {row_value, row_value + (row_value ? ::strlen(row_value) : 0)}; } std::vector<char> extract(sqlite3_stmt* stmt, int columnIndex) const { auto bytes = static_cast<const char*>(sqlite3_column_blob(stmt, columnIndex)); auto len = static_cast<size_t>(sqlite3_column_bytes(stmt, columnIndex)); - return this->go(bytes, len); + return {bytes, bytes + len}; } std::vector<char> extract(sqlite3_value* value) const { auto bytes = static_cast<const char*>(sqlite3_value_blob(value)); auto len = static_cast<size_t>(sqlite3_value_bytes(value)); - return this->go(bytes, len); - } - - protected: - std::vector<char> go(const char* bytes, size_t len) const { - if(len) { - std::vector<char> res; - res.reserve(len); - std::copy(bytes, bytes + len, std::back_inserter(res)); - return res; - } else { - return {}; - } + return {bytes, bytes + len}; } }; @@ -7364,40 +8711,22 @@ namespace sqlite_orm { struct row_extractor<std::tuple<Args...>> { std::tuple<Args...> extract(char** argv) const { - std::tuple<Args...> res; - this->extract<std::tuple_size<decltype(res)>::value>(res, argv); - return res; + return this->extract(argv, std::make_index_sequence<sizeof...(Args)>{}); } std::tuple<Args...> extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { - std::tuple<Args...> res; - this->extract<std::tuple_size<decltype(res)>::value>(res, stmt); - return res; + return this->extract(stmt, std::make_index_sequence<sizeof...(Args)>{}); } protected: - template<size_t I, typename std::enable_if<I != 0>::type* = nullptr> - void extract(std::tuple<Args...>& t, sqlite3_stmt* stmt) const { - using tuple_type = typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type; - std::get<I - 1>(t) = row_extractor<tuple_type>().extract(stmt, I - 1); - this->extract<I - 1>(t, stmt); - } - - template<size_t I, typename std::enable_if<I == 0>::type* = nullptr> - void extract(std::tuple<Args...>&, sqlite3_stmt*) const { - //.. + template<size_t... Idx> + std::tuple<Args...> extract(sqlite3_stmt* stmt, std::index_sequence<Idx...>) const { + return std::tuple<Args...>{row_extractor<Args>{}.extract(stmt, Idx)...}; } - template<size_t I, typename std::enable_if<I != 0>::type* = nullptr> - void extract(std::tuple<Args...>& t, char** argv) const { - using tuple_type = typename std::tuple_element<I - 1, typename std::tuple<Args...>>::type; - std::get<I - 1>(t) = row_extractor<tuple_type>().extract(argv[I - 1]); - this->extract<I - 1>(t, argv); - } - - template<size_t I, typename std::enable_if<I == 0>::type* = nullptr> - void extract(std::tuple<Args...>&, char**) const { - //.. + template<size_t... Idx> + std::tuple<Args...> extract(char** argv, std::index_sequence<Idx...>) const { + return std::tuple<Args...>{row_extractor<Args>{}.extract(argv[Idx])...}; } }; @@ -7411,10 +8740,10 @@ namespace sqlite_orm { if(auto res = internal::journal_mode_from_string(row_value)) { return std::move(*res); } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_journal_mode_string)); + throw std::system_error{orm_error_code::incorrect_journal_mode_string}; } } @@ -7428,44 +8757,130 @@ namespace sqlite_orm { #include <sqlite3.h> #include <string> // std::string -#include <system_error> // std::system_error, std::error_code +#include <utility> // std::move + +// #include "error_code.h" namespace sqlite_orm { + /** + * Escape the provided character in the given string by doubling it. + * @param str A copy of the original string + * @param char2Escape The character to escape + */ + inline std::string sql_escape(std::string str, char char2Escape) { + for(size_t pos = 0; (pos = str.find(char2Escape, pos)) != str.npos; pos += 2) { + str.replace(pos, 1, 2, char2Escape); + } + + return str; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_string_literal(std::string v) { + constexpr char quoteChar = '\''; + return quoteChar + sql_escape(move(v), quoteChar) + quoteChar; + } + + /** + * Quote the given string value using single quotes, + * escape containing single quotes by doubling them. + */ + inline std::string quote_blob_literal(std::string v) { + constexpr char quoteChar = '\''; + return std::string{char('x'), quoteChar} + move(v) + quoteChar; + } + + /** + * Quote the given identifier using double quotes, + * escape containing double quotes by doubling them. + */ + inline std::string quote_identifier(std::string identifier) { + constexpr char quoteChar = '"'; + return quoteChar + sql_escape(move(identifier), quoteChar) + quoteChar; + } + namespace internal { - inline void perform_step(sqlite3* db, sqlite3_stmt* stmt) { - auto rc = sqlite3_step(stmt); - if(rc == SQLITE_DONE) { - // done.. - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + // Wrapper to reduce boiler-plate code + inline sqlite3_stmt* reset_stmt(sqlite3_stmt* stmt) { + sqlite3_reset(stmt); + return stmt; + } + + // note: query is deliberately taken by value, such that it is thrown away early + inline sqlite3_stmt* prepare_stmt(sqlite3* db, std::string query) { + sqlite3_stmt* stmt; + if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + throw_translated_sqlite_error(db); } + return stmt; } - static void perform_void_exec(sqlite3* db, const std::string& query) { + inline void perform_void_exec(sqlite3* db, const std::string& query) { int rc = sqlite3_exec(db, query.c_str(), nullptr, nullptr, nullptr); if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } - template<class T> - inline auto call_insert_impl_and_catch_constraint_failed(const T& insert_impl) { - try { - return insert_impl(); - } catch(const std::system_error& e) { - if(e.code() == std::error_code(SQLITE_CONSTRAINT, get_sqlite_error_category())) { - std::stringstream ss; - ss << "Attempting to execute 'insert' request resulted in an error like \"" << e.what() - << "\". Perhaps ordinary 'insert' is not acceptable for this table and you should try " - "'replace' or 'insert' with explicit column listing?"; - throw std::system_error(e.code(), ss.str()); + inline void perform_exec(sqlite3* db, + const char* query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + int rc = sqlite3_exec(db, query, callback, user_data, nullptr); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(db); + } + } + + inline void perform_exec(sqlite3* db, + const std::string& query, + int (*callback)(void* data, int argc, char** argv, char**), + void* user_data) { + return perform_exec(db, query.c_str(), callback, user_data); + } + + template<int expected = SQLITE_DONE> + void perform_step(sqlite3_stmt* stmt) { + int rc = sqlite3_step(stmt); + if(rc != expected) { + throw_translated_sqlite_error(stmt); + } + } + + template<class L> + void perform_step(sqlite3_stmt* stmt, L&& lambda) { + switch(int rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); } - throw; } } + + template<class L> + void perform_steps(sqlite3_stmt* stmt, L&& lambda) { + int rc; + do { + switch(rc = sqlite3_step(stmt)) { + case SQLITE_ROW: { + lambda(stmt); + } break; + case SQLITE_DONE: + break; + default: { + throw_translated_sqlite_error(stmt); + } + } + } while(rc != SQLITE_DONE); + } } } #pragma once @@ -7531,13 +8946,22 @@ namespace sqlite_orm { } #pragma once -#include <tuple> // std::tuple, std::make_tuple +#include <tuple> // std::tuple, std::make_tuple, std::declval #include <string> // std::string #include <utility> // std::forward +// #include "functional/cxx_universal.h" + +// #include "tuple_helper/tuple_filter.h" + // #include "indexed_column.h" #include <string> // std::string +#include <utility> // std::move + +// #include "functional/cxx_universal.h" + +// #include "ast/where.h" namespace sqlite_orm { @@ -7547,8 +8971,10 @@ namespace sqlite_orm { struct indexed_column_t { using column_type = C; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED indexed_column_t(column_type _column_or_expression) : column_or_expression(std::move(_column_or_expression)) {} +#endif column_type column_or_expression; std::string _collation_name; @@ -7574,29 +9000,19 @@ namespace sqlite_orm { }; template<class C> - struct indexed_column_maker { - using type = indexed_column_t<C>; - - indexed_column_t<C> operator()(C col) const { - return {std::move(col)}; - } - }; + indexed_column_t<C> make_indexed_column(C col) { + return {std::move(col)}; + } template<class C> - struct indexed_column_maker<indexed_column_t<C>> { - using type = indexed_column_t<C>; - - indexed_column_t<C> operator()(indexed_column_t<C> col) const { - return std::move(col); - } - }; + where_t<C> make_indexed_column(where_t<C> wher) { + return std::move(wher); + } template<class C> - auto make_indexed_column(C col) { - indexed_column_maker<C> maker; - return maker(std::move(col)); + indexed_column_t<C> make_indexed_column(indexed_column_t<C> col) { + return std::move(col); } - } /** @@ -7617,6 +9033,10 @@ namespace sqlite_orm { struct index_base { std::string name; bool unique = false; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + index_base(std::string name, bool unique) : name{move(name)}, unique{unique} {} +#endif }; template<class... Els> @@ -7624,27 +9044,39 @@ namespace sqlite_orm { using elements_type = std::tuple<Els...>; using object_type = void; +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED index_t(std::string name_, bool unique_, elements_type elements_) : index_base{move(name_), unique_}, elements(move(elements_)) {} +#endif elements_type elements; }; } template<class... Cols> - internal::index_t<typename internal::indexed_column_maker<Cols>::type...> make_index(const std::string& name, - Cols... cols) { - return {name, false, std::make_tuple(internal::make_indexed_column(cols)...)}; + internal::index_t<decltype(internal::make_indexed_column(std::declval<Cols>()))...> make_index(std::string name, + Cols... cols) { + using cols_tuple = std::tuple<Cols...>; + static_assert(internal::count_tuple<cols_tuple, internal::is_where>::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {move(name), false, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } template<class... Cols> - internal::index_t<typename internal::indexed_column_maker<Cols>::type...> make_unique_index(const std::string& name, - Cols... cols) { - return {name, true, std::make_tuple(internal::make_indexed_column(cols)...)}; + internal::index_t<decltype(internal::make_indexed_column(std::declval<Cols>()))...> + make_unique_index(std::string name, Cols... cols) { + using cols_tuple = std::tuple<Cols...>; + static_assert(internal::count_tuple<cols_tuple, internal::is_where>::value <= 1, + "amount of where arguments can be 0 or 1"); + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {move(name), true, std::make_tuple(internal::make_indexed_column(std::move(cols))...)}); } } #pragma once +#include <type_traits> // std::enable_if, std::is_base_of, std::remove_const + // #include "alias.h" namespace sqlite_orm { @@ -7653,17 +9085,18 @@ namespace sqlite_orm { /** * If T is alias than mapped_type_proxy<T>::type is alias::type - * otherwise T is T. + * otherwise T is unqualified T. */ - template<class T, class sfinae = void> - struct mapped_type_proxy { - using type = T; - }; + template<class T, class SFINAE = void> + struct mapped_type_proxy : std::remove_const<T> {}; template<class T> - struct mapped_type_proxy<T, typename std::enable_if<std::is_base_of<alias_tag, T>::value>::type> { + struct mapped_type_proxy<T, std::enable_if_t<std::is_base_of<alias_tag, T>::value>> { using type = typename T::type; }; + + template<class T> + using mapped_type_proxy_t = typename mapped_type_proxy<T>::type; } } #pragma once @@ -7737,10 +9170,16 @@ namespace sqlite_orm { } #pragma once -#include <type_traits> // std::enable_if, std::is_same, std::decay, std::is_arithmetic +#include <type_traits> // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of #include <tuple> // std::tuple #include <functional> // std::reference_wrapper +// #include "functional/cxx_universal.h" + +// #include "type_traits.h" + +// #include "member_traits/member_traits.h" + // #include "core_functions.h" // #include "select_constraints.h" @@ -7754,398 +9193,236 @@ namespace sqlite_orm { // #include "column.h" // #include "storage_traits.h" -#include <type_traits> // std::is_same, std::enable_if, std::true_type, std::false_type, std::integral_constant -#include <tuple> // std::tuple - -namespace sqlite_orm { - - namespace internal { - - template<class... Ts> - struct storage_impl; - - template<class T, bool WithoutRowId, class... Cs> - struct table_t; - - template<class A, class B> - struct foreign_key_t; - - namespace storage_traits { - - /** - * S - storage_impl type - * T - mapped or not mapped data type - */ - template<class S, class T, class SFINAE = void> - struct type_is_mapped_impl; - - /** - * S - storage - * T - mapped or not mapped data type - */ - template<class S, class T> - struct type_is_mapped : type_is_mapped_impl<typename S::impl_type, T> {}; - /** - * Final specialisation - */ - template<class T> - struct type_is_mapped_impl<storage_impl<>, T, void> : std::false_type {}; - - template<class S, class T> - struct type_is_mapped_impl< - S, - T, - typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> - : std::true_type {}; - - template<class S, class T> - struct type_is_mapped_impl< - S, - T, - typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type> - : type_is_mapped_impl<typename S::super, T> {}; - - /** - * S - storage_impl type - * T - mapped or not mapped data type - */ - template<class S, class T, class SFINAE = void> - struct storage_columns_count_impl; - - /** - * S - storage - * T - mapped or not mapped data type - */ - template<class S, class T> - struct storage_columns_count : storage_columns_count_impl<typename S::impl_type, T> {}; - - /** - * Final specialisation - */ - template<class T> - struct storage_columns_count_impl<storage_impl<>, T, void> : std::integral_constant<int, 0> {}; - - template<class S, class T> - struct storage_columns_count_impl< - S, - T, - typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> - : std::integral_constant<int, S::table_type::elements_count> {}; - - template<class S, class T> - struct storage_columns_count_impl< - S, - T, - typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type> - : storage_columns_count_impl<typename S::super, T> {}; - - /** - * T - table type. - */ - template<class T> - struct table_types; - - /** - * type is std::tuple of field types of mapped colums. - */ - template<class T, bool WithoutRowId, class... Args> - struct table_types<table_t<T, WithoutRowId, Args...>> { - using args_tuple = std::tuple<Args...>; - using columns_tuple = typename tuple_filter<args_tuple, is_column>::type; - - using type = typename tuple_transformer<columns_tuple, column_field_type>::type; - }; - - /** - * S - storage_impl type - * T - mapped or not mapped data type - */ - template<class S, class T, class SFINAE = void> - struct storage_mapped_columns_impl; - - /** - * S - storage - * T - mapped or not mapped data type - */ - template<class S, class T> - struct storage_mapped_columns : storage_mapped_columns_impl<typename S::impl_type, T> {}; +#include <tuple> // std::tuple - /** - * Final specialisation - */ - template<class T> - struct storage_mapped_columns_impl<storage_impl<>, T, void> { - using type = std::tuple<>; - }; +// #include "functional/cxx_type_traits_polyfill.h" - template<class S, class T> - struct storage_mapped_columns_impl< - S, - T, - typename std::enable_if<std::is_same<T, typename S::table_type::object_type>::value>::type> { - using table_type = typename S::table_type; - using type = typename table_types<table_type>::type; - }; +// #include "tuple_helper/tuple_filter.h" - template<class S, class T> - struct storage_mapped_columns_impl< - S, - T, - typename std::enable_if<!std::is_same<T, typename S::table_type::object_type>::value>::type> - : storage_mapped_columns_impl<typename S::super, T> {}; +// #include "tuple_helper/tuple_transformer.h" - /** - * C is any column type: column_t or constraint type - * O - object type references in FOREIGN KEY - */ - template<class C, class O> - struct column_foreign_keys_count : std::integral_constant<int, 0> {}; +#include <tuple> // std::tuple - template<class A, class B, class O> - struct column_foreign_keys_count<foreign_key_t<A, B>, O> { - using target_type = typename foreign_key_t<A, B>::target_type; +// #include "../functional/mpl.h" - static constexpr const int value = std::is_same<O, target_type>::value ? 1 : 0; - }; +namespace sqlite_orm { + namespace internal { - /** - * O - object type references in FOREIGN KEY - * Cs - column types which are stored in table_t::columns_type - */ - template<class O, class... Cs> - struct table_foreign_keys_count_impl; + template<class Tpl, template<class...> class Op> + struct tuple_transformer; - template<class O> - struct table_foreign_keys_count_impl<O> { - static constexpr const int value = 0; - }; + template<class... Types, template<class...> class Op> + struct tuple_transformer<std::tuple<Types...>, Op> { + using type = std::tuple<mpl::invoke_op_t<Op, Types>...>; + }; - template<class O, class H, class... Tail> - struct table_foreign_keys_count_impl<O, H, Tail...> { - static constexpr const int value = - column_foreign_keys_count<H, O>::value + table_foreign_keys_count_impl<O, Tail...>::value; - }; + /* + * Transform specified tuple. + * + * `Op` is a metafunction operation. + */ + template<class Tpl, template<class...> class Op> + using transform_tuple_t = typename tuple_transformer<Tpl, Op>::type; + } +} - /** - * T is table_t type - * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) - */ - template<class T, class O> - struct table_foreign_keys_count; +// #include "type_traits.h" - template<class T, class... Cs, class O> - struct table_foreign_keys_count<table_t<T, false, Cs...>, O> { - using table_type = table_t<T, false, Cs...>; +// #include "storage_lookup.h" - static constexpr const int value = table_foreign_keys_count_impl<O, Cs...>::value; - }; +#include <type_traits> // std::true_type, std::false_type, std::remove_const, std::enable_if +#include <tuple> +#include <utility> // std::index_sequence - /** - * S - storage class - * O - type mapped to S - */ - template<class S, class O> - struct storage_foreign_keys_count_impl; +// #include "functional/cxx_universal.h" - template<class O> - struct storage_foreign_keys_count_impl<storage_impl<>, O> : std::integral_constant<int, 0> {}; +// #include "functional/cxx_type_traits_polyfill.h" - template<class H, class... Ts, class O> - struct storage_foreign_keys_count_impl<storage_impl<H, Ts...>, O> { - static constexpr const int value = table_foreign_keys_count<H, O>::value + - storage_foreign_keys_count_impl<storage_impl<Ts...>, O>::value; - }; +// #include "type_traits.h" - /** - * S - storage class - * O - type mapped to S - * This class tells how many types mapped to S have foreign keys to O - */ - template<class S, class O> - struct storage_foreign_keys_count { - using impl_type = typename S::impl_type; +namespace sqlite_orm { + namespace internal { - static constexpr const int value = storage_foreign_keys_count_impl<impl_type, O>::value; - }; + template<class... DBO> + struct storage_t; - /** - * C is any table element type: column_t or constraint type - * O - object type references in FOREIGN KEY - */ - template<class C, class O, class SFINAE = void> - struct column_fk_references { - using type = std::tuple<>; - }; + template<class... DBO> + using db_objects_tuple = std::tuple<DBO...>; - template<class C, class O, class SFINAE = void> - struct column_foreign_keys { - using type = std::tuple<>; - }; + template<class T> + struct is_storage : std::false_type {}; - template<class A, class B, class O> - struct column_foreign_keys< - foreign_key_t<A, B>, - O, - typename std::enable_if<std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { - using type = std::tuple<foreign_key_t<A, B>>; - }; + template<class... DBO> + struct is_storage<storage_t<DBO...>> : std::true_type {}; + template<class... DBO> + struct is_storage<const storage_t<DBO...>> : std::true_type {}; - template<class A, class B, class O> - struct column_foreign_keys< - foreign_key_t<A, B>, - O, - typename std::enable_if<!std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { - using type = std::tuple<>; - }; + template<class T> + struct is_db_objects : std::false_type {}; - template<class A, class B, class O> - struct column_fk_references< - foreign_key_t<A, B>, - O, - typename std::enable_if<std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { - using target_type = typename foreign_key_t<A, B>::source_type; + template<class... DBO> + struct is_db_objects<db_objects_tuple<DBO...>> : std::true_type {}; + template<class... DBO> + struct is_db_objects<const db_objects_tuple<DBO...>> : std::true_type {}; - using type = std::tuple<target_type>; - }; + /** + * std::true_type if given object is mapped, std::false_type otherwise. + * + * Note: unlike table_t<>, index_t<>::object_type and trigger_t<>::object_type is always void. + */ + template<typename DBO, typename Lookup> + struct object_type_matches : polyfill::conjunction<polyfill::negation<std::is_void<object_type_t<DBO>>>, + std::is_same<Lookup, object_type_t<DBO>>> {}; - template<class A, class B, class O> - struct column_fk_references< - foreign_key_t<A, B>, - O, - typename std::enable_if<!std::is_same<O, typename foreign_key_t<A, B>::target_type>::value>::type> { - using type = std::tuple<>; - }; + /** + * std::true_type if given lookup type (object) is mapped, std::false_type otherwise. + */ + template<typename DBO, typename Lookup> + struct lookup_type_matches : polyfill::disjunction<object_type_matches<DBO, Lookup>> {}; + } - /** - * O - object type references in FOREIGN KEY - * Cs - column types which are stored in table_t::columns_type - */ - template<class O, class... Cs> - struct table_fk_references_impl; + // pick/lookup metafunctions + namespace internal { - template<class O, class... Cs> - struct table_foreign_keys_impl; + /** + * Indirect enabler for DBO, accepting an index to disambiguate non-unique DBOs + */ + template<class Lookup, size_t Ix, class DBO> + struct enable_found_table : std::enable_if<lookup_type_matches<DBO, Lookup>::value, DBO> {}; - template<class O> - struct table_fk_references_impl<O> { - using type = std::tuple<>; - }; + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - mapped data type + * Seq - index sequence matching the number of DBOs + * DBOs - db_objects_tuple type + */ + template<class Lookup, class Seq, class DBOs> + struct storage_pick_table; - template<class O> - struct table_foreign_keys_impl<O> { - using type = std::tuple<>; - }; + template<class Lookup, size_t... Ix, class... DBO> + struct storage_pick_table<Lookup, std::index_sequence<Ix...>, db_objects_tuple<DBO...>> + : enable_found_table<Lookup, Ix, DBO>... {}; - template<class O, class H, class... Tail> - struct table_fk_references_impl<O, H, Tail...> { - using head_tuple = typename column_fk_references<H, O>::type; - using tail_tuple = typename table_fk_references_impl<O, Tail...>::type; - using type = typename conc_tuple<head_tuple, tail_tuple>::type; - }; + /** + * SFINAE friendly facility to pick a table definition (`table_t`) from a tuple of database objects. + * + * Lookup - 'table' type, mapped data type + * DBOs - db_objects_tuple type, possibly const-qualified + */ + template<class Lookup, class DBOs> + using storage_pick_table_t = typename storage_pick_table<Lookup, + std::make_index_sequence<std::tuple_size<DBOs>::value>, + std::remove_const_t<DBOs>>::type; - template<class O, class H, class... Tail> - struct table_foreign_keys_impl<O, H, Tail...> { - using head_tuple = typename column_foreign_keys<H, O>::type; - using tail_tuple = typename table_foreign_keys_impl<O, Tail...>::type; - using type = typename conc_tuple<head_tuple, tail_tuple>::type; - }; + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type + * Lookup - mapped data type + */ + template<class Lookup, class DBOs> + struct storage_find_table : polyfill::detected_or<polyfill::nonesuch, storage_pick_table_t, Lookup, DBOs> {}; - /** - * T is table_t type - * O is object type which is the reference target (e.g. foreign_key(&Visit::userId).references(&User::id) has O = User) - */ - template<class T, class O> - struct table_fk_references; + /** + * Find a table definition (`table_t`) from a tuple of database objects; + * `std::nonesuch` if not found. + * + * DBOs - db_objects_tuple type, possibly const-qualified + * Lookup - mapped data type + */ + template<class Lookup, class DBOs> + using storage_find_table_t = typename storage_find_table<Lookup, std::remove_const_t<DBOs>>::type; + +#ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION + template<class DBOs, class Lookup, class SFINAE = void> + struct is_mapped : std::false_type {}; + template<class DBOs, class Lookup> + struct is_mapped<DBOs, Lookup, polyfill::void_t<storage_pick_table_t<Lookup, DBOs>>> : std::true_type {}; +#else + template<class DBOs, class Lookup, class SFINAE = storage_find_table_t<Lookup, DBOs>> + struct is_mapped : std::true_type {}; + template<class DBOs, class Lookup> + struct is_mapped<DBOs, Lookup, polyfill::nonesuch> : std::false_type {}; +#endif - template<class T, class O> - struct table_foreign_keys; + template<class DBOs, class Lookup> + SQLITE_ORM_INLINE_VAR constexpr bool is_mapped_v = is_mapped<DBOs, Lookup>::value; + } +} - template<class T, bool WithoutRowId, class... Cs, class O> - struct table_fk_references<table_t<T, WithoutRowId, Cs...>, O> { - using table_type = table_t<T, WithoutRowId, Cs...>; +// runtime lookup functions +namespace sqlite_orm { + namespace internal { + /** + * Pick the table definition for the specified lookup type from the given tuple of schema objects. + * + * Note: This function requires Lookup to be mapped, otherwise it is removed from the overload resolution set. + */ + template<class Lookup, class DBOs, satisfies<is_db_objects, DBOs> = true> + auto& pick_table(DBOs& dbObjects) { + using table_type = storage_pick_table_t<Lookup, DBOs>; + return std::get<table_type>(dbObjects); + } + } +} - using type = typename table_fk_references_impl<O, Cs...>::type; - }; +namespace sqlite_orm { - template<class T, bool WithoutRowId, class... Cs, class O> - struct table_foreign_keys<table_t<T, WithoutRowId, Cs...>, O> { - using table_type = table_t<T, WithoutRowId, Cs...>; + namespace internal { - using type = typename table_foreign_keys_impl<O, Cs...>::type; - }; + namespace storage_traits { /** - * S - storage class - * O - type mapped to S + * DBO - db object (table) */ - template<class S, class O> - struct storage_fk_references_impl; - - template<class S, class O> - struct storage_foreign_keys_impl; - - template<class O> - struct storage_fk_references_impl<storage_impl<>, O> { - using type = std::tuple<>; - }; + template<class DBO> + struct storage_mapped_columns_impl + : tuple_transformer<filter_tuple_t<elements_type_t<DBO>, is_column>, field_type_t> {}; - template<class O> - struct storage_foreign_keys_impl<storage_impl<>, O> { + template<> + struct storage_mapped_columns_impl<polyfill::nonesuch> { using type = std::tuple<>; }; - template<class H, class... Ts, class O> - struct storage_fk_references_impl<storage_impl<H, Ts...>, O> { - using head_tuple = typename table_fk_references<H, O>::type; - using tail_tuple = typename storage_fk_references_impl<storage_impl<Ts...>, O>::type; - using type = typename conc_tuple<head_tuple, tail_tuple>::type; - }; - - template<class H, class... Ts, class O> - struct storage_foreign_keys_impl<storage_impl<H, Ts...>, O> { - using head_tuple = typename table_foreign_keys<H, O>::type; - using tail_tuple = typename storage_foreign_keys_impl<storage_impl<Ts...>, O>::type; - using type = typename conc_tuple<head_tuple, tail_tuple>::type; - }; - /** - * S - storage class - * O - type mapped to S - * type holds `std::tuple` with types that has references to O as foreign keys + * DBOs - db_objects_tuple type + * Lookup - mapped or unmapped data type */ - template<class S, class O> - struct storage_fk_references { - using impl_type = typename S::impl_type; - - using type = typename storage_fk_references_impl<impl_type, O>::type; - }; - - template<class S, class O> - struct storage_foreign_keys { - using impl_type = typename S::impl_type; - - using type = typename storage_foreign_keys_impl<impl_type, O>::type; - }; - + template<class DBOs, class Lookup> + struct storage_mapped_columns : storage_mapped_columns_impl<storage_find_table_t<Lookup, DBOs>> {}; } } } // #include "function.h" -#include <string> // std::string #include <sqlite3.h> +#include <type_traits> +#include <string> // std::string #include <tuple> // std::tuple #include <functional> // std::function +#include <algorithm> // std::min +#include <utility> // std::move, std::forward + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { struct arg_values; + template<class T, class P> + struct pointer_arg; + template<class T, class P, class D> + class pointer_binding; + namespace internal { - struct function_base { + struct user_defined_function_base { using func_call = std::function< void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; using final_call = std::function<void(sqlite3_context* context, void* functionPointer)>; @@ -8155,101 +9432,67 @@ namespace sqlite_orm { std::function<int*()> create; void (*destroy)(int*) = nullptr; - function_base(decltype(name) name_, - decltype(argumentsCount) argumentsCount_, - decltype(create) create_, - decltype(destroy) destroy_) : +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + user_defined_function_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : name(move(name_)), argumentsCount(argumentsCount_), create(move(create_)), destroy(destroy_) {} +#endif }; - struct scalar_function_t : function_base { + struct user_defined_scalar_function_t : user_defined_function_base { func_call run; - scalar_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(run) run_, - decltype(destroy) destroy_) : - function_base{move(name_), argumentsCount_, move(create_), destroy_}, + user_defined_scalar_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + user_defined_function_base{move(name_), argumentsCount_, move(create_), destroy_}, run(move(run_)) {} }; - struct aggregate_function_t : function_base { + struct user_defined_aggregate_function_t : user_defined_function_base { func_call step; final_call finalCall; - aggregate_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(step) step_, - decltype(finalCall) finalCall_, - decltype(destroy) destroy_) : - function_base{move(name_), argumentsCount_, move(create_), destroy_}, + user_defined_aggregate_function_t(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + user_defined_function_base{move(name_), argumentsCount_, move(create_), destroy_}, step(move(step_)), finalCall(move(finalCall_)) {} }; - // got it from here https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature - template<class F> - struct is_scalar_function_impl { - - template<class U, class T> - struct SFINAE; - - template<class U, class R> - struct SFINAE<U, R (U::*)() const> {}; - - template<class U> - static char test(SFINAE<U, decltype(&U::operator())>*); - - template<class U> - static int test(...); - - static constexpr bool has = sizeof(test<F>(0)) == sizeof(char); - }; - template<class F> - struct is_aggregate_function_impl { - - template<class U, class T> - struct SFINAE; - - template<class U, class R> - struct SFINAE<U, R (U::*)() const> {}; - - template<class U> - static char test(SFINAE<U, decltype(&U::step)>*); - - template<class U> - static int test(...); - - template<class U> - static char test2(SFINAE<U, decltype(&U::fin)>*); - - template<class U> - static int test2(...); - - static constexpr bool has = sizeof(test<F>(0)) == sizeof(char); - static constexpr bool has2 = sizeof(test2<F>(0)) == sizeof(char); - static constexpr bool has_both = has && has2; - }; + using scalar_call_function_t = decltype(&F::operator()); template<class F> - struct is_scalar_function : std::integral_constant<bool, is_scalar_function_impl<F>::has> {}; + using aggregate_step_function_t = decltype(&F::step); template<class F> - struct is_aggregate_function : std::integral_constant<bool, is_aggregate_function_impl<F>::has_both> {}; + using aggregate_fin_function_t = decltype(&F::fin); + template<class F, class = void> + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v = false; template<class F> - struct scalar_run_member_pointer { - using type = decltype(&F::operator()); - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v<F, polyfill::void_t<scalar_call_function_t<F>>> = + true; + template<class F, class = void> + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v = false; template<class F> - struct aggregate_run_member_pointer { - using step_type = decltype(&F::step); - using fin_type = decltype(&F::fin); - }; + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v< + F, + polyfill::void_t<aggregate_step_function_t<F>, + aggregate_fin_function_t<F>, + std::enable_if_t<std::is_member_function_pointer<aggregate_step_function_t<F>>::value>, + std::enable_if_t<std::is_member_function_pointer<aggregate_fin_function_t<F>>::value>>> = + true; template<class T> struct member_function_arguments; @@ -8257,37 +9500,34 @@ namespace sqlite_orm { template<class O, class R, class... Args> struct member_function_arguments<R (O::*)(Args...) const> { using member_function_type = R (O::*)(Args...) const; - using tuple_type = std::tuple<typename std::decay<Args>::type...>; + using tuple_type = std::tuple<std::decay_t<Args>...>; using return_type = R; }; template<class O, class R, class... Args> struct member_function_arguments<R (O::*)(Args...)> { using member_function_type = R (O::*)(Args...); - using tuple_type = std::tuple<typename std::decay<Args>::type...>; + using tuple_type = std::tuple<std::decay_t<Args>...>; using return_type = R; }; - template<class F, bool IsScalar> + template<class F, class SFINAE = void> struct callable_arguments_impl; template<class F> - struct callable_arguments_impl<F, true> { - using function_member_pointer = typename scalar_run_member_pointer<F>::type; - using args_tuple = typename member_function_arguments<function_member_pointer>::tuple_type; - using return_type = typename member_function_arguments<function_member_pointer>::return_type; + struct callable_arguments_impl<F, std::enable_if_t<is_scalar_function_v<F>>> { + using args_tuple = typename member_function_arguments<scalar_call_function_t<F>>::tuple_type; + using return_type = typename member_function_arguments<scalar_call_function_t<F>>::return_type; }; template<class F> - struct callable_arguments_impl<F, false> { - using step_function_member_pointer = typename aggregate_run_member_pointer<F>::step_type; - using fin_function_member_pointer = typename aggregate_run_member_pointer<F>::fin_type; - using args_tuple = typename member_function_arguments<step_function_member_pointer>::tuple_type; - using return_type = typename member_function_arguments<fin_function_member_pointer>::return_type; + struct callable_arguments_impl<F, std::enable_if_t<is_aggregate_function_v<F>>> { + using args_tuple = typename member_function_arguments<aggregate_step_function_t<F>>::tuple_type; + using return_type = typename member_function_arguments<aggregate_fin_function_t<F>>::return_type; }; template<class F> - struct callable_arguments : callable_arguments_impl<F, is_scalar_function<F>::value> {}; + struct callable_arguments : callable_arguments_impl<F> {}; template<class F, class... Args> struct function_call { @@ -8296,6 +9536,93 @@ namespace sqlite_orm { args_tuple args; }; + + template<class T> + struct unpacked_arg { + using type = T; + }; + template<class F, class... Args> + struct unpacked_arg<function_call<F, Args...>> { + using type = typename callable_arguments<F>::return_type; + }; + template<class T> + using unpacked_arg_t = typename unpacked_arg<T>::type; + + template<size_t I, class FnArg, class CallArg> + SQLITE_ORM_CONSTEVAL bool expected_pointer_value() { + static_assert(polyfill::always_false_v<FnArg, CallArg>, "Expected a pointer value for I-th argument"); + return false; + } + + template<size_t I, class FnArg, class CallArg, class EnableIfTag = void> + constexpr bool is_same_pvt_v = expected_pointer_value<I, FnArg, CallArg>(); + + // Always allow binding nullptr to a pointer argument + template<size_t I, class PointerArg> + constexpr bool is_same_pvt_v<I, PointerArg, nullptr_t, polyfill::void_t<typename PointerArg::tag>> = true; + +#if __cplusplus >= 201703L // using C++17 or higher + template<size_t I, const char* PointerArg, const char* Binding> + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding == PointerArg; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template<size_t I, class PointerArg, class Binding> + constexpr bool + is_same_pvt_v<I, PointerArg, Binding, polyfill::void_t<typename PointerArg::tag, typename Binding::tag>> = + assert_same_pointer_type<I, PointerArg::tag::value, Binding::tag::value>(); +#else + template<size_t I, class PointerArg, class Binding> + SQLITE_ORM_CONSTEVAL bool assert_same_pointer_type() { + constexpr bool valid = Binding::value == PointerArg::value; + static_assert(valid, "Pointer value types of I-th argument do not match"); + return valid; + } + + template<size_t I, class PointerArg, class Binding> + constexpr bool + is_same_pvt_v<I, PointerArg, Binding, polyfill::void_t<typename PointerArg::tag, typename Binding::tag>> = + assert_same_pointer_type<I, typename PointerArg::tag, typename Binding::tag>(); +#endif + + template<size_t I, class FnArg, class CallArg> + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::false_type) { + return true; + } + + template<size_t I, class FnArg, class CallArg> + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_type(std::true_type) { + return is_same_pvt_v<I, FnArg, CallArg>; + } + + template<class FnArgs, class CallArgs> + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant<size_t(-1)>) { + return true; + } + template<class FnArgs, class CallArgs, size_t I> + SQLITE_ORM_CONSTEVAL bool validate_pointer_value_types(polyfill::index_constant<I>) { + using func_arg_t = std::tuple_element_t<I, FnArgs>; + using passed_arg_t = unpacked_arg_t<std::tuple_element_t<I, CallArgs>>; + +#ifdef SQLITE_ORM_RELAXED_CONSTEXPR_SUPPORTED + constexpr bool valid = validate_pointer_value_type<I, + std::tuple_element_t<I, FnArgs>, + unpacked_arg_t<std::tuple_element_t<I, CallArgs>>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v<func_arg_t, pointer_arg>) || + (polyfill::is_specialization_of_v<passed_arg_t, pointer_binding>) > {}); + + return validate_pointer_value_types<FnArgs, CallArgs>(polyfill::index_constant<I - 1>{}) && valid; +#else + return validate_pointer_value_types<FnArgs, CallArgs>(polyfill::index_constant<I - 1>{}) && + validate_pointer_value_type<I, + std::tuple_element_t<I, FnArgs>, + unpacked_arg_t<std::tuple_element_t<I, CallArgs>>>( + polyfill::bool_constant < (polyfill::is_specialization_of_v<func_arg_t, pointer_arg>) || + (polyfill::is_specialization_of_v<passed_arg_t, pointer_binding>) > {}); +#endif + } } /** @@ -8307,10 +9634,12 @@ namespace sqlite_orm { using function_args_tuple = typename internal::callable_arguments<F>::args_tuple; constexpr auto argsCount = std::tuple_size<args_tuple>::value; constexpr auto functionArgsCount = std::tuple_size<function_args_tuple>::value; - static_assert( - (argsCount == functionArgsCount && !std::is_same<function_args_tuple, std::tuple<arg_values>>::value) || - std::is_same<function_args_tuple, std::tuple<arg_values>>::value, - "Arguments amount does not match"); + static_assert((argsCount == functionArgsCount && + !std::is_same<function_args_tuple, std::tuple<arg_values>>::value && + internal::validate_pointer_value_types<function_args_tuple, args_tuple>( + polyfill::index_constant<std::min<>(functionArgsCount, argsCount) - 1>{})) || + std::is_same<function_args_tuple, std::tuple<arg_values>>::value, + "Number of arguments does not match"); return {std::make_tuple(std::forward<Args>(args)...)}; } @@ -8326,291 +9655,503 @@ namespace sqlite_orm { * for different types. E.g. specialization for internal::length_t has `type` int cause * LENGTH returns INTEGER in sqlite. Every column_result_t must have `type` type that equals * c++ SELECT return type for T + * DBOs - db_objects_tuple type * T - C++ type * SFINAE - sfinae argument */ - template<class St, class T, class SFINAE = void> + template<class DBOs, class T, class SFINAE = void> struct column_result_t; + template<class DBOs, class T> + using column_result_of_t = typename column_result_t<DBOs, T>::type; + #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<class St, class T> - struct column_result_t<St, as_optional_t<T>, void> { - using type = std::optional<typename column_result_t<St, T>::type>; + template<class DBOs, class T> + struct column_result_t<DBOs, as_optional_t<T>, void> { + using type = std::optional<column_result_of_t<DBOs, T>>; }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - - template<class St, class O, class F> - struct column_result_t<St, - F O::*, - typename std::enable_if<std::is_member_pointer<F O::*>::value && - !std::is_member_function_pointer<F O::*>::value>::type> { - using type = F; + template<class DBOs, class T> + struct column_result_t<DBOs, std::optional<T>, void> { + using type = std::optional<T>; }; +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED - template<class St, class L, class A> - struct column_result_t<St, dynamic_in_t<L, A>, void> { + template<class DBOs, class L, class A> + struct column_result_t<DBOs, dynamic_in_t<L, A>, void> { using type = bool; }; - template<class St, class L, class... Args> - struct column_result_t<St, in_t<L, Args...>, void> { + template<class DBOs, class L, class... Args> + struct column_result_t<DBOs, in_t<L, Args...>, void> { using type = bool; }; - /** - * Common case for all getter types. Getter types are defined in column.h file - */ - template<class St, class T> - struct column_result_t<St, T, typename std::enable_if<is_getter<T>::value>::type> { - using type = typename getter_traits<T>::field_type; - }; + template<class DBOs, class T> + struct column_result_t<DBOs, T, match_if<std::is_member_pointer, T>> : member_field_type<T> {}; - /** - * Common case for all setter types. Setter types are defined in column.h file - */ - template<class St, class T> - struct column_result_t<St, T, typename std::enable_if<is_setter<T>::value>::type> { - using type = typename setter_traits<T>::field_type; + template<class DBOs, class R, class S, class... Args> + struct column_result_t<DBOs, built_in_function_t<R, S, Args...>, void> { + using type = R; }; - template<class St, class R, class S, class... Args> - struct column_result_t<St, built_in_function_t<R, S, Args...>, void> { + template<class DBOs, class R, class S, class... Args> + struct column_result_t<DBOs, built_in_aggregate_function_t<R, S, Args...>, void> { using type = R; }; - template<class St, class F, class... Args> - struct column_result_t<St, function_call<F, Args...>, void> { + template<class DBOs, class F, class... Args> + struct column_result_t<DBOs, function_call<F, Args...>, void> { using type = typename callable_arguments<F>::return_type; }; - template<class St, class X, class S> - struct column_result_t<St, built_in_function_t<internal::unique_ptr_result_of<X>, S, X>, void> { - using type = std::unique_ptr<typename column_result_t<St, X>::type>; + template<class DBOs, class X, class... Rest, class S> + struct column_result_t<DBOs, built_in_function_t<internal::unique_ptr_result_of<X>, S, X, Rest...>, void> { + using type = std::unique_ptr<column_result_of_t<DBOs, X>>; }; - template<class St, class T> - struct column_result_t<St, count_asterisk_t<T>, void> { - using type = int; + template<class DBOs, class X, class S> + struct column_result_t<DBOs, built_in_aggregate_function_t<internal::unique_ptr_result_of<X>, S, X>, void> { + using type = std::unique_ptr<column_result_of_t<DBOs, X>>; }; - template<class St> - struct column_result_t<St, count_asterisk_without_type, void> { + template<class DBOs, class T> + struct column_result_t<DBOs, count_asterisk_t<T>, void> { using type = int; }; - template<class St, class T> - struct column_result_t<St, distinct_t<T>, void> { - using type = typename column_result_t<St, T>::type; + template<class DBOs> + struct column_result_t<DBOs, nullptr_t, void> { + using type = nullptr_t; }; - template<class St, class T> - struct column_result_t<St, all_t<T>, void> { - using type = typename column_result_t<St, T>::type; + template<class DBOs> + struct column_result_t<DBOs, count_asterisk_without_type, void> { + using type = int; }; - template<class St, class L, class R> - struct column_result_t<St, conc_t<L, R>, void> { + template<class DBOs, class T> + struct column_result_t<DBOs, distinct_t<T>, void> : column_result_t<DBOs, T> {}; + + template<class DBOs, class T> + struct column_result_t<DBOs, all_t<T>, void> : column_result_t<DBOs, T> {}; + + template<class DBOs, class L, class R> + struct column_result_t<DBOs, conc_t<L, R>, void> { using type = std::string; }; - template<class St, class L, class R> - struct column_result_t<St, add_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, add_t<L, R>, void> { using type = double; }; - template<class St, class L, class R> - struct column_result_t<St, sub_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, sub_t<L, R>, void> { using type = double; }; - template<class St, class L, class R> - struct column_result_t<St, mul_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, mul_t<L, R>, void> { using type = double; }; - template<class St, class L, class R> - struct column_result_t<St, internal::div_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, internal::div_t<L, R>, void> { using type = double; }; - template<class St, class L, class R> - struct column_result_t<St, mod_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, mod_t<L, R>, void> { using type = double; }; - template<class St, class L, class R> - struct column_result_t<St, bitwise_shift_left_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, bitwise_shift_left_t<L, R>, void> { using type = int; }; - template<class St, class L, class R> - struct column_result_t<St, bitwise_shift_right_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, bitwise_shift_right_t<L, R>, void> { using type = int; }; - template<class St, class L, class R> - struct column_result_t<St, bitwise_and_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, bitwise_and_t<L, R>, void> { using type = int; }; - template<class St, class L, class R> - struct column_result_t<St, bitwise_or_t<L, R>, void> { + template<class DBOs, class L, class R> + struct column_result_t<DBOs, bitwise_or_t<L, R>, void> { using type = int; }; - template<class St, class T> - struct column_result_t<St, bitwise_not_t<T>, void> { + template<class DBOs, class T> + struct column_result_t<DBOs, bitwise_not_t<T>, void> { using type = int; }; - template<class St> - struct column_result_t<St, rowid_t, void> { + template<class DBOs> + struct column_result_t<DBOs, rowid_t, void> { using type = int64; }; - template<class St> - struct column_result_t<St, oid_t, void> { + template<class DBOs> + struct column_result_t<DBOs, oid_t, void> { using type = int64; }; - template<class St> - struct column_result_t<St, _rowid_t, void> { + template<class DBOs> + struct column_result_t<DBOs, _rowid_t, void> { using type = int64; }; - template<class St, class T> - struct column_result_t<St, table_rowid_t<T>, void> { + template<class DBOs, class T> + struct column_result_t<DBOs, table_rowid_t<T>, void> { using type = int64; }; - template<class St, class T> - struct column_result_t<St, table_oid_t<T>, void> { + template<class DBOs, class T> + struct column_result_t<DBOs, table_oid_t<T>, void> { using type = int64; }; - template<class St, class T> - struct column_result_t<St, table__rowid_t<T>, void> { + template<class DBOs, class T> + struct column_result_t<DBOs, table__rowid_t<T>, void> { using type = int64; }; - template<class St, class T, class C> - struct column_result_t<St, alias_column_t<T, C>, void> { - using type = typename column_result_t<St, C>::type; - }; + template<class DBOs, class T, class C> + struct column_result_t<DBOs, alias_column_t<T, C>, void> : column_result_t<DBOs, C> {}; - template<class St, class T, class F> - struct column_result_t<St, column_pointer<T, F>> : column_result_t<St, F, void> {}; + template<class DBOs, class T, class F> + struct column_result_t<DBOs, column_pointer<T, F>, void> : column_result_t<DBOs, F> {}; - template<class St, class... Args> - struct column_result_t<St, columns_t<Args...>, void> { - using type = std::tuple<typename column_result_t<St, typename std::decay<Args>::type>::type...>; + template<class DBOs, class... Args> + struct column_result_t<DBOs, columns_t<Args...>, void> { + using type = std::tuple<column_result_of_t<DBOs, std::decay_t<Args>>...>; }; - template<class St, class T, class... Args> - struct column_result_t<St, select_t<T, Args...>> : column_result_t<St, T, void> {}; + template<class DBOs, class T, class... Args> + struct column_result_t<DBOs, select_t<T, Args...>> : column_result_t<DBOs, T> {}; - template<class St, class T> - struct column_result_t<St, T, typename std::enable_if<is_base_of_template<T, compound_operator>::value>::type> { - using left_type = typename T::left_type; - using right_type = typename T::right_type; - using left_result = typename column_result_t<St, left_type>::type; - using right_result = typename column_result_t<St, right_type>::type; + template<class DBOs, class T> + struct column_result_t<DBOs, T, std::enable_if_t<is_base_of_template_v<T, compound_operator>>> { + using left_result = column_result_of_t<DBOs, typename T::left_type>; + using right_result = column_result_of_t<DBOs, typename T::right_type>; static_assert(std::is_same<left_result, right_result>::value, "Compound subselect queries must return same types"); using type = left_result; }; - template<class St, class T> - struct column_result_t<St, T, typename std::enable_if<is_base_of_template<T, binary_condition>::value>::type> { + template<class DBOs, class T> + struct column_result_t<DBOs, T, std::enable_if_t<is_base_of_template_v<T, binary_condition>>> { using type = typename T::result_type; }; /** * Result for the most simple queries like `SELECT 1` */ - template<class St, class T> - struct column_result_t<St, T, typename std::enable_if<std::is_arithmetic<T>::value>::type> { + template<class DBOs, class T> + struct column_result_t<DBOs, T, match_if<std::is_arithmetic, T>> { using type = T; }; /** * Result for the most simple queries like `SELECT 'ototo'` */ - template<class St> - struct column_result_t<St, const char*, void> { + template<class DBOs> + struct column_result_t<DBOs, const char*, void> { using type = std::string; }; - template<class St> - struct column_result_t<St, std::string, void> { + template<class DBOs> + struct column_result_t<DBOs, std::string, void> { using type = std::string; }; - template<class St, class T, class E> - struct column_result_t<St, as_t<T, E>, void> : column_result_t<St, typename std::decay<E>::type, void> {}; + template<class DBOs, class T, class E> + struct column_result_t<DBOs, as_t<T, E>, void> : column_result_t<DBOs, std::decay_t<E>> {}; - template<class St, class T> - struct column_result_t<St, asterisk_t<T>, void> { - using type = typename storage_traits::storage_mapped_columns<St, T>::type; - }; + template<class DBOs, class T> + struct column_result_t<DBOs, asterisk_t<T>, match_if_not<std::is_base_of, alias_tag, T>> + : storage_traits::storage_mapped_columns<DBOs, T> {}; - template<class St, class T> - struct column_result_t<St, object_t<T>, void> { + template<class DBOs, class A> + struct column_result_t<DBOs, asterisk_t<A>, match_if<std::is_base_of, alias_tag, A>> + : storage_traits::storage_mapped_columns<DBOs, type_t<A>> {}; + + template<class DBOs, class T> + struct column_result_t<DBOs, object_t<T>, void> { using type = T; }; - template<class St, class T, class E> - struct column_result_t<St, cast_t<T, E>, void> { + template<class DBOs, class T, class E> + struct column_result_t<DBOs, cast_t<T, E>, void> { using type = T; }; - template<class St, class R, class T, class E, class... Args> - struct column_result_t<St, simple_case_t<R, T, E, Args...>, void> { + template<class DBOs, class R, class T, class E, class... Args> + struct column_result_t<DBOs, simple_case_t<R, T, E, Args...>, void> { using type = R; }; - template<class St, class A, class T, class E> - struct column_result_t<St, like_t<A, T, E>, void> { + template<class DBOs, class A, class T, class E> + struct column_result_t<DBOs, like_t<A, T, E>, void> { using type = bool; }; - template<class St, class A, class T> - struct column_result_t<St, glob_t<A, T>, void> { + template<class DBOs, class A, class T> + struct column_result_t<DBOs, glob_t<A, T>, void> { using type = bool; }; - template<class St, class C> - struct column_result_t<St, negated_condition_t<C>, void> { + template<class DBOs, class C> + struct column_result_t<DBOs, negated_condition_t<C>, void> { using type = bool; }; - template<class St, class T> - struct column_result_t<St, std::reference_wrapper<T>, void> : column_result_t<St, T, void> {}; + template<class DBOs, class T> + struct column_result_t<DBOs, std::reference_wrapper<T>, void> : column_result_t<DBOs, T> {}; } } #pragma once #include <string> // std::string -#include <type_traits> // std::remove_reference, std::is_same, std::is_base_of +#include <type_traits> // std::remove_reference, std::is_same, std::decay #include <vector> // std::vector #include <tuple> // std::tuple_size, std::tuple_element -#include <algorithm> // std::reverse, std::find_if +#include <utility> // std::forward, std::move -// #include "column_result.h" +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/static_magic.h" + +#include <type_traits> // std::false_type, std::true_type, std::integral_constant +#include <utility> // std::forward + +namespace sqlite_orm { + + // got from here + // https://stackoverflow.com/questions/37617677/implementing-a-compile-time-static-if-logic-for-different-string-types-in-a-co + namespace internal { + + template<class R = void> + decltype(auto) empty_callable() { + static auto res = [](auto&&...) -> R { + return R(); + }; + return (res); + } + +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + template<bool B, typename T, typename F> + decltype(auto) static_if([[maybe_unused]] T&& trueFn, [[maybe_unused]] F&& falseFn) { + if constexpr(B) { + return std::forward<T>(trueFn); + } else { + return std::forward<F>(falseFn); + } + } + + template<bool B, typename T> + decltype(auto) static_if([[maybe_unused]] T&& trueFn) { + if constexpr(B) { + return std::forward<T>(trueFn); + } else { + return empty_callable(); + } + } + + template<bool B, typename L, typename... Args> + void call_if_constexpr([[maybe_unused]] L&& lambda, [[maybe_unused]] Args&&... args) { + if constexpr(B) { + lambda(std::forward<Args>(args)...); + } + } +#else + template<typename T, typename F> + decltype(auto) static_if(std::true_type, T&& trueFn, const F&) { + return std::forward<T>(trueFn); + } + + template<typename T, typename F> + decltype(auto) static_if(std::false_type, const T&, F&& falseFn) { + return std::forward<F>(falseFn); + } + + template<bool B, typename T, typename F> + decltype(auto) static_if(T&& trueFn, F&& falseFn) { + return static_if(std::integral_constant<bool, B>{}, std::forward<T>(trueFn), std::forward<F>(falseFn)); + } + + template<bool B, typename T> + decltype(auto) static_if(T&& trueFn) { + return static_if(std::integral_constant<bool, B>{}, std::forward<T>(trueFn), empty_callable()); + } + + template<bool B, typename L, typename... Args> + void call_if_constexpr(L&& lambda, Args&&... args) { + static_if<B>(std::forward<L>(lambda))(std::forward<Args>(args)...); + } +#endif + } + +} + +// #include "functional/mpl.h" + +// #include "functional/index_sequence_util.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_iteration.h" + +#include <tuple> // std::tuple, std::get, std::tuple_element, std::tuple_size +#include <type_traits> // std::index_sequence, std::make_index_sequence +#include <utility> // std::forward, std::move + +// #include "../functional/cxx_universal.h" + +// #include "../functional/cxx_type_traits_polyfill.h" + +// #include "../functional/cxx_functional_polyfill.h" + +// #include "../functional/index_sequence_util.h" + +namespace sqlite_orm { + namespace internal { + + // got it form here https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer + template<class Function, class FunctionPointer, class Tuple, size_t... I> + auto call_impl(Function& f, FunctionPointer functionPointer, Tuple t, std::index_sequence<I...>) { + return (f.*functionPointer)(std::get<I>(move(t))...); + } + + template<class Function, class FunctionPointer, class Tuple> + auto call(Function& f, FunctionPointer functionPointer, Tuple t) { + constexpr size_t size = std::tuple_size<Tuple>::value; + return call_impl(f, functionPointer, move(t), std::make_index_sequence<size>{}); + } + + template<class Function, class Tuple> + auto call(Function& f, Tuple t) { + return call(f, &Function::operator(), move(t)); + } + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && defined(SQLITE_ORM_IF_CONSTEXPR_SUPPORTED) + template<bool reversed = false, class Tpl, size_t... Idx, class L> + void iterate_tuple(const Tpl& tpl, std::index_sequence<Idx...>, L&& lambda) { + if constexpr(reversed) { + iterate_tuple(tpl, reverse_index_sequence(std::index_sequence<Idx...>{}), std::forward<L>(lambda)); + } else { + (lambda(std::get<Idx>(tpl)), ...); + } + } +#else + template<bool reversed = false, class Tpl, class L> + void iterate_tuple(const Tpl& /*tpl*/, std::index_sequence<>, L&& /*lambda*/) {} -// #include "static_magic.h" + template<bool reversed = false, class Tpl, size_t I, size_t... Idx, class L> + void iterate_tuple(const Tpl& tpl, std::index_sequence<I, Idx...>, L&& lambda) { +#ifdef SQLITE_ORM_IF_CONSTEXPR_SUPPORTED + if constexpr(reversed) { +#else + if(reversed) { +#endif + iterate_tuple<reversed>(tpl, std::index_sequence<Idx...>{}, std::forward<L>(lambda)); + lambda(std::get<I>(tpl)); + } else { + lambda(std::get<I>(tpl)); + iterate_tuple<reversed>(tpl, std::index_sequence<Idx...>{}, std::forward<L>(lambda)); + } + } +#endif + template<bool reversed = false, class Tpl, class L> + void iterate_tuple(const Tpl& tpl, L&& lambda) { + iterate_tuple<reversed>(tpl, + std::make_index_sequence<std::tuple_size<Tpl>::value>{}, + std::forward<L>(lambda)); + } + +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template<class Tpl, size_t... Idx, class L> + void iterate_tuple(std::index_sequence<Idx...>, L&& lambda) { + (lambda((std::tuple_element_t<Idx, Tpl>*)nullptr), ...); + } +#else + template<class Tpl, class L> + void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} + + template<class Tpl, size_t I, size_t... Idx, class L> + void iterate_tuple(std::index_sequence<I, Idx...>, L&& lambda) { + lambda((std::tuple_element_t<I, Tpl>*)nullptr); + iterate_tuple<Tpl>(std::index_sequence<Idx...>{}, std::forward<L>(lambda)); + } +#endif + template<class Tpl, class L> + void iterate_tuple(L&& lambda) { + iterate_tuple<Tpl>(std::make_index_sequence<std::tuple_size<Tpl>::value>{}, std::forward<L>(lambda)); + } + + template<class R, class Tpl, size_t... Idx, class Projection = polyfill::identity> + R create_from_tuple(Tpl&& tpl, std::index_sequence<Idx...>, Projection project = {}) { + return R{polyfill::invoke(project, std::get<Idx>(std::forward<Tpl>(tpl)))...}; + } + + template<class R, class Tpl, class Projection = polyfill::identity> + R create_from_tuple(Tpl&& tpl, Projection project = {}) { + return create_from_tuple<R>( + std::forward<Tpl>(tpl), + std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tpl>>::value>{}, + std::forward<Projection>(project)); + } + + template<template<class...> class Base, class L> + struct lambda_as_template_base : L { +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + lambda_as_template_base(L&& lambda) : L{std::move(lambda)} {} +#endif + template<class... T> + decltype(auto) operator()(const Base<T...>& object) { + return L::operator()(object); + } + }; + + /* + * This method wraps the specified callable in another function object, + * which in turn implicitly casts its single argument to the specified template base class, + * then passes the converted argument to the lambda. + * + * Note: This method is useful for reducing combinatorial instantiation of template lambdas, + * as long as this library supports compilers that do not implement + * explicit template parameters in generic lambdas [SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED]. + * Unfortunately it doesn't work with user-defined conversion operators in order to extract + * parts of a class. In other words, the destination type must be a direct template base class. + */ + template<template<class...> class Base, class L> + lambda_as_template_base<Base, L> call_as_template_base(L lambda) { + return {std::move(lambda)}; + } + } +} + +// #include "member_traits/member_traits.h" // #include "typed_comparator.h" -// #include "constraints.h" +// #include "type_traits.h" -// #include "tuple_helper/tuple_helper.h" +// #include "constraints.h" // #include "table_info.h" -// #include "type_printer.h" - // #include "column.h" namespace sqlite_orm { @@ -8626,74 +10167,97 @@ namespace sqlite_orm { }; /** - * Table class. + * Table definition. */ template<class T, bool WithoutRowId, class... Cs> struct table_t : basic_table { - using super = basic_table; using object_type = T; using elements_type = std::tuple<Cs...>; - static constexpr const int elements_count = static_cast<int>(std::tuple_size<elements_type>::value); - static constexpr const bool is_without_rowid = WithoutRowId; + static constexpr bool is_without_rowid_v = WithoutRowId; + using is_without_rowid = polyfill::bool_constant<is_without_rowid_v>; elements_type elements; - table_t(std::string name_, elements_type elements_) : super{move(name_)}, elements{move(elements_)} {} +#ifndef SQLITE_ORM_AGGREGATE_BASES_SUPPORTED + table_t(std::string name_, elements_type elements_) : basic_table{move(name_)}, elements{move(elements_)} {} +#endif table_t<T, true, Cs...> without_rowid() const { return {this->name, this->elements}; } /** - * Function used to get field value from object by mapped member pointer/setter/getter + * Returns foreign keys count in table definition */ - template<class F, class C> - const F* get_object_field_pointer(const object_type& obj, C c) const { - const F* res = nullptr; - this->for_each_column_with_field_type<F>([&res, &c, &obj](auto& col) { - using column_type = typename std::remove_reference<decltype(col)>::type; - using member_pointer_t = typename column_type::member_pointer_t; - using getter_type = typename column_type::getter_type; - using setter_type = typename column_type::setter_type; - // Make static_if have at least one input as a workaround for GCC bug: - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 - if(!res) { - static_if<std::is_same<C, member_pointer_t>{}>([&res, &obj, &col](const C& c_) { - if(compare_any(col.member_pointer, c_)) { - res = &(obj.*col.member_pointer); - } - })(c); - } - if(!res) { - static_if<std::is_same<C, getter_type>{}>([&res, &obj, &col](const C& c_) { - if(compare_any(col.getter, c_)) { - res = &((obj).*(col.getter))(); - } - })(c); - } - if(!res) { - static_if<std::is_same<C, setter_type>{}>([&res, &obj, &col](const C& c_) { - if(compare_any(col.setter, c_)) { - res = &((obj).*(col.getter))(); - } - })(c); - } - }); + constexpr int foreign_keys_count() const { +#if SQLITE_VERSION_NUMBER >= 3006019 + using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>; + return int(fk_index_sequence::size()); +#else + return 0; +#endif + } + + /** + * Function used to get field value from object by mapped member pointer/setter/getter. + * + * For a setter the corresponding getter has to be searched, + * so the method returns a pointer to the field as returned by the found getter. + * Otherwise the method invokes the member pointer and returns its result. + */ + template<class M, satisfies_not<is_setter, M> = true> + decltype(auto) object_field_value(const object_type& object, M memberPointer) const { + return polyfill::invoke(memberPointer, object); + } + + template<class M, satisfies<is_setter, M> = true> + const member_field_type_t<M>* object_field_value(const object_type& object, M memberPointer) const { + using field_type = member_field_type_t<M>; + const field_type* res = nullptr; + iterate_tuple(this->elements, + col_index_sequence_with_field_type<elements_type, field_type>{}, + call_as_template_base<column_field>([&res, &memberPointer, &object](const auto& column) { + if(compare_any(column.setter, memberPointer)) { + res = &polyfill::invoke(column.member_pointer, object); + } + })); return res; } - template<class C> - bool exists_in_composite_primary_key(const C& column) const { - auto res = false; + const basic_generated_always::storage_type* + find_column_generated_storage_type(const std::string& name) const { + const basic_generated_always::storage_type* result = nullptr; +#if SQLITE_VERSION_NUMBER >= 3031000 + iterate_tuple(this->elements, + col_index_sequence_with<elements_type, is_generated_always>{}, + [&result, &name](auto& column) { + if(column.name != name) { + return; + } + using generated_op_index_sequence = + filter_tuple_sequence_t<std::remove_const_t<decltype(column.constraints)>, + is_generated_always>; + constexpr size_t opIndex = first_index_sequence_value(generated_op_index_sequence{}); + result = &get<opIndex>(column.constraints).storage; + }); +#endif + return result; + } + + template<class G, class S> + bool exists_in_composite_primary_key(const column_field<G, S>& column) const { + bool res = false; this->for_each_primary_key([&column, &res](auto& primaryKey) { - iterate_tuple(primaryKey.columns, [&res, &column](auto& value) { - if(!res) { - if(column.member_pointer) { - res = compare_any(value, column.member_pointer); - } else { - res = compare_any(value, column.getter) || compare_any(value, column.setter); - } + using colrefs_tuple = decltype(primaryKey.columns); + using same_type_index_sequence = + filter_tuple_sequence_t<colrefs_tuple, + check_if_is_type<member_field_type_t<G>>::template fn, + member_field_type_t>; + iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { + if(compare_any(memberPointer, column.member_pointer) || + compare_any(memberPointer, column.setter)) { + res = true; } }); }); @@ -8701,668 +10265,339 @@ namespace sqlite_orm { } /** - * Calls **l** with every primary key dedicated constraint + * Call passed lambda with all defined primary keys. */ template<class L> - void for_each_primary_key(const L& lambda) const { - iterate_tuple(this->elements, [&lambda](auto& element) { - using element_type = typename std::decay<decltype(element)>::type; - static_if<is_primary_key<element_type>{}>(lambda)(element); - }); + void for_each_primary_key(L&& lambda) const { + using pk_index_sequence = filter_tuple_sequence_t<elements_type, is_primary_key>; + iterate_tuple(this->elements, pk_index_sequence{}, lambda); } std::vector<std::string> composite_key_columns_names() const { std::vector<std::string> res; - this->for_each_primary_key([this, &res](auto& c) { - res = this->composite_key_columns_names(c); + this->for_each_primary_key([this, &res](auto& primaryKey) { + res = this->composite_key_columns_names(primaryKey); }); return res; } std::vector<std::string> primary_key_column_names() const { - std::vector<std::string> res; - this->for_each_column_with<primary_key_t<>>([&res](auto& c) { - res.push_back(c.name); - }); - if(!res.size()) { - res = this->composite_key_columns_names(); + using pkcol_index_sequence = col_index_sequence_with<elements_type, is_primary_key>; + + if(pkcol_index_sequence::size() > 0) { + return create_from_tuple<std::vector<std::string>>(this->elements, + pkcol_index_sequence{}, + &column_identifier::name); + } else { + return this->composite_key_columns_names(); } - return res; } - template<class... Args> - std::vector<std::string> composite_key_columns_names(const primary_key_t<Args...>& pk) const { - std::vector<std::string> res; - using pk_columns_tuple = decltype(pk.columns); - res.reserve(std::tuple_size<pk_columns_tuple>::value); - iterate_tuple(pk.columns, [this, &res](auto& v) { - if(auto columnName = this->find_column_name(v)) { - res.push_back(*columnName); - } else { - res.push_back({}); - } + template<class L> + void for_each_primary_key_column(L&& lambda) const { + iterate_tuple(this->elements, + col_index_sequence_with<elements_type, is_primary_key>{}, + call_as_template_base<column_field>([&lambda](const auto& column) { + lambda(column.member_pointer); + })); + this->for_each_primary_key([&lambda](auto& primaryKey) { + iterate_tuple(primaryKey.columns, lambda); }); - return res; + } + + template<class... Args> + std::vector<std::string> composite_key_columns_names(const primary_key_t<Args...>& primaryKey) const { + return create_from_tuple<std::vector<std::string>>(primaryKey.columns, + [this, empty = std::string{}](auto& memberPointer) { + if(const std::string* columnName = + this->find_column_name(memberPointer)) { + return *columnName; + } else { + return empty; + } + }); } /** * Searches column name by class member pointer passed as the first argument. * @return column name or empty string if nothing found. */ - template<class F, - class O, - typename = typename std::enable_if<std::is_member_pointer<F O::*>::value && - !std::is_member_function_pointer<F O::*>::value>::type> - const std::string* find_column_name(F O::*m) const { + template<class M, satisfies<std::is_member_pointer, M> = true> + const std::string* find_column_name(M m) const { const std::string* res = nullptr; - this->template for_each_column_with_field_type<F>([&res, m](auto& c) { - if(c.member_pointer == m) { - res = &c.name; - } - }); + using field_type = member_field_type_t<M>; + iterate_tuple(this->elements, + col_index_sequence_with_field_type<elements_type, field_type>{}, + [&res, m](auto& c) { + if(compare_any(c.member_pointer, m) || compare_any(c.setter, m)) { + res = &c.name; + } + }); return res; } /** - * Searches column name by class getter function member pointer passed as first argument. - * @return column name or empty string if nothing found. + * Counts and returns amount of columns without GENERATED ALWAYS constraints. Skips table constraints. */ - template<class G> - const std::string* find_column_name(G getter, - typename std::enable_if<is_getter<G>::value>::type* = nullptr) const { - const std::string* res = nullptr; - using field_type = typename getter_traits<G>::field_type; - this->template for_each_column_with_field_type<field_type>([&res, getter](auto& c) { - if(compare_any(c.getter, getter)) { - res = &c.name; - } - }); - return res; + constexpr int non_generated_columns_count() const { +#if SQLITE_VERSION_NUMBER >= 3031000 + using non_generated_col_index_sequence = + col_index_sequence_excluding<elements_type, is_generated_always>; + return int(non_generated_col_index_sequence::size()); +#else + return this->count_columns_amount(); +#endif } /** - * Searches column name by class setter function member pointer passed as first argument. - * @return column name or empty string if nothing found. + * Counts and returns amount of columns. Skips constraints. */ - template<class S> - const std::string* find_column_name(S setter, - typename std::enable_if<is_setter<S>::value>::type* = nullptr) const { - const std::string* res = nullptr; - using field_type = typename setter_traits<S>::field_type; - this->template for_each_column_with_field_type<field_type>([&res, setter](auto& c) { - if(compare_any(c.setter, setter)) { - res = &c.name; - } - }); - return res; - } - - int count_columns_amount() const { - auto res = 0; - this->for_each_column([&res](auto&) { - ++res; - }); - return res; + constexpr int count_columns_amount() const { + using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>; + return int(col_index_sequence::size()); } /** - * Iterates all columns and fires passed lambda. Lambda must have one and only templated argument. Otherwise - * code will not compile. Excludes table constraints (e.g. foreign_key_t) at the end of the columns list. To - * iterate columns with table constraints use iterate_tuple(columns, ...) instead. L is lambda type. Do - * not specify it explicitly. - * @param lambda Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} + * Call passed lambda with all defined foreign keys. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` */ template<class L> - void for_each_column(const L& lambda) const { - iterate_tuple(this->elements, [&lambda](auto& element) { - using element_type = typename std::decay<decltype(element)>::type; - static_if<is_column<element_type>{}>(lambda)(element); - }); + void for_each_foreign_key(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>; + iterate_tuple(this->elements, fk_index_sequence{}, lambda); } - template<class L> - void for_each_foreign_key(const L& lambda) const { - iterate_tuple(this->elements, [&lambda](auto& element) { - using element_type = typename std::decay<decltype(element)>::type; - static_if<is_foreign_key<element_type>{}>(lambda)(element); - }); + template<class O, class L> + void for_each_foreign_key_to(L&& lambda) const { + using fk_index_sequence = filter_tuple_sequence_t<elements_type, is_foreign_key>; + using filtered_index_sequence = filter_tuple_sequence_t<elements_type, + check_if_is_type<O>::template fn, + target_type_t, + fk_index_sequence>; + iterate_tuple(this->elements, filtered_index_sequence{}, lambda); } - template<class F, class L> - void for_each_column_with_field_type(const L& lambda) const { - this->for_each_column([&lambda](auto& column) { - using column_type = typename std::decay<decltype(column)>::type; - using field_type = typename column_field_type<column_type>::type; - static_if<std::is_same<F, field_type>{}>(lambda)(column); - }); + /** + * Call passed lambda with all defined columns. + * @param lambda Lambda called for each column. Function signature: `void(auto& column)` + */ + template<class L> + void for_each_column(L&& lambda) const { + using col_index_sequence = filter_tuple_sequence_t<elements_type, is_column>; + iterate_tuple(this->elements, col_index_sequence{}, lambda); } /** - * Iterates all columns that have specified constraints and fires passed lambda. - * Lambda must have one and only templated argument Otherwise code will not compile. - * L is lambda type. Do not specify it explicitly. - * @param lambda Lambda to be called per column itself. Must have signature like this [] (auto col) -> void {} + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. */ - template<class Op, class L> - void for_each_column_with(const L& lambda) const { - this->for_each_column([&lambda](auto& column) { - using tuple_helper::tuple_contains_type; - using column_type = typename std::decay<decltype(column)>::type; - using constraints_type = typename column_constraints_type<column_type>::type; - static_if<tuple_contains_type<Op, constraints_type>{}>(lambda)(column); - }); + template<template<class...> class OpTraitFn, class L> + void for_each_column_excluding(L&& lambda) const { + iterate_tuple(this->elements, col_index_sequence_excluding<elements_type, OpTraitFn>{}, lambda); } - std::vector<table_info> get_table_info() const { - std::vector<table_info> res; - res.reserve(size_t(this->elements_count)); - this->for_each_column([&res](auto& col) { - std::string dft; - using field_type = typename std::decay<decltype(col)>::type::field_type; - if(auto d = col.default_value()) { - dft = *d; - } - table_info i{ - -1, - col.name, - type_printer<field_type>().print(), - col.not_null(), - dft, - col.template has<primary_key_t<>>(), - }; - res.emplace_back(i); - }); - auto compositeKeyColumnNames = this->composite_key_columns_names(); - for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { - auto& columnName = compositeKeyColumnNames[i]; - auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_info& ti) { - return ti.name == columnName; - }); - if(it != res.end()) { - it->pk = static_cast<int>(i + 1); - } - } - return res; + /** + * Call passed lambda with columns not having the specified constraint trait `OpTrait`. + * @param lambda Lambda called for each column. + */ + template<class OpTraitFnCls, class L, satisfies<mpl::is_metafunction_class, OpTraitFnCls> = true> + void for_each_column_excluding(L&& lambda) const { + this->for_each_column_excluding<OpTraitFnCls::template fn>(lambda); } + + std::vector<table_xinfo> get_table_info() const; }; + + template<class T> + struct is_table : std::false_type {}; + + template<class O, bool W, class... Cs> + struct is_table<table_t<O, W, Cs...>> : std::true_type {}; } /** - * Function used for table creation. Do not use table constructor - use this function - * cause table class is templated and its constructing too (just like std::make_unique or std::make_pair). + * Factory function for a table definition. + * + * The mapped object type is determined implicitly from the first column definition. */ - template<class... Cs, class T = typename std::tuple_element<0, std::tuple<Cs...>>::type::object_type> - internal::table_t<T, false, Cs...> make_table(const std::string& name, Cs... args) { - return {name, std::make_tuple<Cs...>(std::forward<Cs>(args)...)}; + template<class... Cs, class T = typename std::tuple_element_t<0, std::tuple<Cs...>>::object_type> + internal::table_t<T, false, Cs...> make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {move(name), std::make_tuple<Cs...>(std::forward<Cs>(args)...)}); } + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ template<class T, class... Cs> - internal::table_t<T, false, Cs...> make_table(const std::string& name, Cs... args) { - return {name, std::make_tuple<Cs...>(std::forward<Cs>(args)...)}; + internal::table_t<T, false, Cs...> make_table(std::string name, Cs... args) { + SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES( + return {move(name), std::make_tuple<Cs...>(std::forward<Cs>(args)...)}); } } #pragma once -#include <string> // std::string -#include <sqlite3.h> -#include <cstddef> // std::nullptr_t -#include <system_error> // std::system_error, std::error_code -#include <sstream> // std::stringstream -#include <stdlib.h> // std::atoi -#include <type_traits> // std::forward, std::enable_if, std::is_same, std::remove_reference, std::false_type, std::true_type -#include <utility> // std::pair, std::make_pair -#include <vector> // std::vector -#include <algorithm> // std::find_if +#include <tuple> // std::tuple_size #include <typeindex> // std::type_index +#include <string> // std::string +#include <type_traits> // std::is_same, std::decay, std::make_index_sequence, std::index_sequence -// #include "error_code.h" - -// #include "statement_finalizer.h" +// #include "functional/cxx_universal.h" -// #include "row_extractor.h" +// #include "functional/static_magic.h" -// #include "util.h" +// #include "tuple_helper/tuple_iteration.h" -// #include "constraints.h" +// #include "type_traits.h" // #include "select_constraints.h" -// #include "field_printer.h" - -// #include "table_info.h" - -// #include "sync_schema_result.h" - -// #include "field_value_holder.h" - -#include <type_traits> // std::enable_if - -// #include "column.h" +// #include "storage_lookup.h" +// interface functions namespace sqlite_orm { namespace internal { - template<class T, class SFINAE = void> - struct field_value_holder; - - template<class T> - struct field_value_holder<T, typename std::enable_if<getter_traits<T>::returns_lvalue>::type> { - using type = typename getter_traits<T>::field_type; - - const type& value; - }; - - template<class T> - struct field_value_holder<T, typename std::enable_if<!getter_traits<T>::returns_lvalue>::type> { - using type = typename getter_traits<T>::field_type; + template<class DBOs> + using tables_index_sequence = filter_tuple_sequence_t<DBOs, is_table>; - type value; - }; - } -} - -namespace sqlite_orm { - - namespace internal { - - struct storage_impl_base { - - bool table_exists(const std::string& tableName, sqlite3* db) const { - auto result = false; - std::stringstream ss; - ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = '" - << "table" - << "' AND name = '" << tableName << "'"; - auto query = ss.str(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char** /*azColName*/) -> int { - auto& res = *(bool*)data; - if(argc) { - res = !!std::atoi(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return result; - } - - void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { - std::stringstream ss; - ss << "ALTER TABLE " << oldName << " RENAME TO " << newName; - perform_void_exec(db, ss.str()); - } - - static bool calculate_remove_add_columns(std::vector<table_info*>& columnsToAdd, - std::vector<table_info>& storageTableInfo, - std::vector<table_info>& dbTableInfo) { - bool notEqual = false; - - // iterate through storage columns - for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); - ++storageColumnInfoIndex) { + template<class DBOs, satisfies<is_db_objects, DBOs> = true> + int foreign_keys_count(const DBOs& dbObjects) { + int res = 0; + iterate_tuple<true>(dbObjects, tables_index_sequence<DBOs>{}, [&res](const auto& table) { + res += table.foreign_keys_count(); + }); + return res; + } - // get storage's column info - auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto& columnName = storageColumnInfo.name; + template<class Lookup, class DBOs, satisfies<is_db_objects, DBOs> = true> + auto lookup_table(const DBOs& dbObjects) { + return static_if<is_mapped_v<DBOs, Lookup>>( + [](const auto& dbObjects) { + return &pick_table<Lookup>(dbObjects); + }, + empty_callable<nullptr_t>())(dbObjects); + } - // search for a column in db eith the same name - auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { - return ti.name == columnName; - }); - if(dbColumnInfoIt != dbTableInfo.end()) { - auto& dbColumnInfo = *dbColumnInfoIt; - auto columnsAreEqual = - dbColumnInfo.name == storageColumnInfo.name && - dbColumnInfo.notnull == storageColumnInfo.notnull && - (dbColumnInfo.dflt_value.length() > 0) == (storageColumnInfo.dflt_value.length() > 0) && - dbColumnInfo.pk == storageColumnInfo.pk; - if(!columnsAreEqual) { - notEqual = true; - break; - } - dbTableInfo.erase(dbColumnInfoIt); - storageTableInfo.erase(storageTableInfo.begin() + - static_cast<ptrdiff_t>(storageColumnInfoIndex)); - --storageColumnInfoIndex; - } else { - columnsToAdd.push_back(&storageColumnInfo); - } + template<class DBOs, satisfies<is_db_objects, DBOs> = true> + std::string find_table_name(const DBOs& dbObjects, const std::type_index& ti) { + std::string res; + iterate_tuple<true>(dbObjects, tables_index_sequence<DBOs>{}, [&ti, &res](const auto& table) { + using table_type = std::decay_t<decltype(table)>; + if(ti == typeid(object_type_t<table_type>)) { + res = table.name; } - return notEqual; - } + }); + return res; + } - std::vector<table_info> get_table_info(const std::string& tableName, sqlite3* db) const { - std::vector<table_info> result; - auto query = "PRAGMA table_info('" + tableName + "')"; - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char**) -> int { - auto& res = *(std::vector<table_info>*)data; - if(argc) { - auto index = 0; - auto cid = std::atoi(argv[index++]); - std::string name = argv[index++]; - std::string type = argv[index++]; - bool notnull = !!std::atoi(argv[index++]); - std::string dflt_value = argv[index] ? argv[index] : ""; - index++; - auto pk = std::atoi(argv[index++]); - res.push_back(table_info(cid, name, type, notnull, dflt_value, pk)); - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - return result; - } - }; + template<class Lookup, class DBOs, satisfies<is_db_objects, DBOs> = true> + std::string lookup_table_name(const DBOs& dbObjects) { + return static_if<is_mapped_v<DBOs, Lookup>>( + [](const auto& dbObjects) { + return pick_table<Lookup>(dbObjects).name; + }, + empty_callable<std::string>())(dbObjects); + } /** - * This is a generic implementation. Used as a tail in storage_impl inheritance chain + * Find column name by its type and member pointer. */ - template<class... Ts> - struct storage_impl; - - template<class H, class... Ts> - struct storage_impl<H, Ts...> : public storage_impl<Ts...> { - using table_type = H; - using super = storage_impl<Ts...>; - - storage_impl(H h, Ts... ts) : super(std::forward<Ts>(ts)...), table(std::move(h)) {} - - table_type table; - - template<class L> - void for_each(const L& l) { - this->super::for_each(l); - l(*this); - } - -#if SQLITE_VERSION_NUMBER >= 3006019 - - /** - * Returns foreign keys count in table definition - */ - int foreign_keys_count() { - auto res = 0; - iterate_tuple(this->table.elements, [&res](auto& c) { - if(is_foreign_key<typename std::decay<decltype(c)>::type>::value) { - ++res; - } - }); - return res; - } - -#endif - - /** - * Is used to get column name by member pointer to a base class. - * Main difference between `column_name` and `column_name_simple` is that - * `column_name` has SFINAE check for type equality but `column_name_simple` has not. - */ - template<class O, class F> - const std::string* column_name_simple(F O::*m) const { - return this->table.find_column_name(m); - } - - /** - * Cute function used to find column name by its type and member pointer. Uses SFINAE to - * skip inequal type O. - */ - template<class O, class F, class HH = typename H::object_type> - const std::string* column_name(F O::*m, - typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) const { - return this->table.find_column_name(m); - } - - /** - * Opposite version of function defined above. Just calls same function in superclass. - */ - template<class O, class F, class HH = typename H::object_type> - const std::string* - column_name(F O::*m, typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) const { - return this->super::column_name(m); - } - - template<class T, class F, class HH = typename H::object_type> - const std::string* column_name(const column_pointer<T, F>& c, - typename std::enable_if<std::is_same<T, HH>::value>::type* = nullptr) const { - return this->column_name_simple(c.field); - } - - template<class T, class F, class HH = typename H::object_type> - const std::string* - column_name(const column_pointer<T, F>& c, - typename std::enable_if<!std::is_same<T, HH>::value>::type* = nullptr) const { - return this->super::column_name(c); - } - - template<class O, class HH = typename H::object_type> - const auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) const { - return *this; - } - - template<class O, class HH = typename H::object_type> - const auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) const { - return this->super::template get_impl<O>(); - } - - template<class O, class HH = typename H::object_type> - auto& get_impl(typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) { - return *this; - } - - template<class O, class HH = typename H::object_type> - auto& get_impl(typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) { - return this->super::template get_impl<O>(); - } - - template<class O, class HH = typename H::object_type> - const auto* find_table(typename std::enable_if<std::is_same<O, HH>::value>::type* = nullptr) const { - return &this->table; - } - - template<class O, class HH = typename H::object_type> - const auto* find_table(typename std::enable_if<!std::is_same<O, HH>::value>::type* = nullptr) const { - return this->super::template find_table<O>(); - } - - std::string find_table_name(std::type_index ti) const { - std::type_index thisTypeIndex{typeid(typename H::object_type)}; - if(thisTypeIndex == ti) { - return this->table.name; - } else { - return this->super::find_table_name(ti); - } - } - - void add_column(const table_info& ti, sqlite3* db) const { - std::stringstream ss; - ss << "ALTER TABLE " << this->table.name << " ADD COLUMN " << ti.name; - ss << " " << ti.type; - if(ti.pk) { - ss << " PRIMARY KEY"; - } - if(ti.notnull) { - ss << " NOT NULL"; - } - if(ti.dflt_value.length()) { - ss << " DEFAULT " << ti.dflt_value; - } - perform_void_exec(db, ss.str()); - } - - /** - * Copies current table to another table with a given **name**. - * Performs CREATE TABLE %name% AS SELECT %this->table.columns_names()% FROM &this->table.name%; - */ - void - copy_table(sqlite3* db, const std::string& name, const std::vector<table_info*>& columnsToIgnore) const { - std::stringstream ss; - std::vector<std::string> columnNames; - this->table.for_each_column([&columnNames, &columnsToIgnore](auto& c) { - auto& columnName = c.name; - auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), - columnsToIgnore.end(), - [&columnName](auto tableInfoPointer) { - return columnName == tableInfoPointer->name; - }); - if(columnToIgnoreIt == columnsToIgnore.end()) { - columnNames.emplace_back(columnName); - } - }); - auto columnNamesCount = columnNames.size(); - ss << "INSERT INTO " << name << " ("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << columnNames[i]; - if(i < columnNamesCount - 1) { - ss << ","; - } - ss << " "; - } - ss << ") "; - ss << "SELECT "; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << columnNames[i]; - if(i < columnNamesCount - 1) { - ss << ", "; - } - } - ss << " FROM '" << this->table.name << "' "; - perform_void_exec(db, ss.str()); - } - - sync_schema_result schema_status(sqlite3* db, bool preserve) const { - - auto res = sync_schema_result::already_in_sync; - - // first let's see if table with such name exists.. - auto gottaCreateTable = !this->table_exists(this->table.name, db); - if(!gottaCreateTable) { - - // get table info provided in `make_table` call.. - auto storageTableInfo = this->table.get_table_info(); - - // now get current table info from db using `PRAGMA table_info` query.. - auto dbTableInfo = this->get_table_info(this->table.name, db); + template<class O, class F, class DBOs, satisfies<is_db_objects, DBOs> = true> + const std::string* find_column_name(const DBOs& dbObjects, F O::*field) { + return pick_table<O>(dbObjects).find_column_name(field); + } - // this vector will contain pointers to columns that gotta be added.. - std::vector<table_info*> columnsToAdd; + /** + * Materialize column pointer: + * 1. by explicit object type and member pointer. + */ + template<class O, class F, class DBOs, satisfies<is_db_objects, DBOs> = true> + constexpr decltype(auto) materialize_column_pointer(const DBOs&, const column_pointer<O, F>& cp) { + return cp.field; + } - if(this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { - gottaCreateTable = true; - } + /** + * Find column name by: + * 1. by explicit object type and member pointer. + */ + template<class O, class F, class DBOs, satisfies<is_db_objects, DBOs> = true> + const std::string* find_column_name(const DBOs& dbObjects, const column_pointer<O, F>& cp) { + auto field = materialize_column_pointer(dbObjects, cp); + return pick_table<O>(dbObjects).find_column_name(field); + } + } +} +#pragma once - if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are - // excess columns at the db.. - if(dbTableInfo.size() > 0) { - // extra table columns than storage columns - if(!preserve) { - gottaCreateTable = true; - } else { - res = decltype(res)::old_columns_removed; - } - } - } - if(gottaCreateTable) { - res = decltype(res)::dropped_and_recreated; - } else { - if(columnsToAdd.size()) { - // extra storage columns than table columns - for(auto columnPointer: columnsToAdd) { - if(columnPointer->notnull && columnPointer->dflt_value.empty()) { - gottaCreateTable = true; - break; - } - } - if(!gottaCreateTable) { - if(res == decltype(res)::old_columns_removed) { - res = decltype(res)::new_columns_added_and_old_columns_removed; - } else { - res = decltype(res)::new_columns_added; - } - } else { - res = decltype(res)::dropped_and_recreated; - } - } else { - if(res != decltype(res)::old_columns_removed) { - res = decltype(res)::already_in_sync; - } - } - } - } else { - res = decltype(res)::new_table_created; - } - return res; - } +#include <string> // std::string - private: - using self = storage_impl<H, Ts...>; - }; +// #include "constraints.h" - template<> - struct storage_impl<> : storage_impl_base { +// #include "serializer_context.h" - std::string find_table_name(std::type_index) const { - return {}; - } +// #include "storage_lookup.h" - template<class L> - void for_each(const L&) {} +namespace sqlite_orm { - int foreign_keys_count() { - return 0; - } + namespace internal { - template<class O> - const void* find_table() const { - return nullptr; - } - }; + template<class T, class I> + std::string serialize(const T& t, const serializer_context<I>& context); + /** + * Serialize default value of a column's default valu + */ template<class T> - struct is_storage_impl : std::false_type {}; + std::string serialize_default_value(const default_t<T>& dft) { + db_objects_tuple<> dbObjects; + serializer_context<db_objects_tuple<>> context{dbObjects}; + return serialize(dft.value, context); + } - template<class... Ts> - struct is_storage_impl<storage_impl<Ts...>> : std::true_type {}; } + } #pragma once -#include <memory> // std::unique/shared_ptr, std::make_unique/shared -#include <string> // std::string #include <sqlite3.h> +#include <memory> // std::unique_ptr/shared_ptr, std::make_unique/std::make_shared +#include <system_error> // std::system_error +#include <string> // std::string #include <type_traits> // std::remove_reference, std::is_base_of, std::decay, std::false_type, std::true_type -#include <cstddef> // std::ptrdiff_t -#include <iterator> // std::input_iterator_tag, std::iterator_traits, std::distance -#include <functional> // std::function +#include <functional> // std::identity #include <sstream> // std::stringstream #include <map> // std::map #include <vector> // std::vector -#include <tuple> // std::tuple_size, std::tuple, std::make_tuple +#include <tuple> // std::tuple_size, std::tuple, std::make_tuple, std::tie #include <utility> // std::forward, std::pair -#include <algorithm> // std::find +#include <algorithm> // std::for_each, std::ranges::for_each +// #include "functional/cxx_optional.h" -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/static_magic.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_traits.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "type_traits.h" // #include "alias.h" // #include "row_extractor_builder.h" +// #include "functional/cxx_universal.h" + // #include "row_extractor.h" // #include "mapped_row_extractor.h" @@ -9372,6 +10607,9 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" #include <sqlite3.h> +#include <type_traits> // std::is_member_object_pointer + +// #include "functional/static_magic.h" // #include "row_extractor.h" @@ -9381,7 +10619,11 @@ namespace sqlite_orm { struct object_from_column_builder_base { sqlite3_stmt* stmt = nullptr; - mutable int index = 0; + int index = 0; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + object_from_column_builder_base(sqlite3_stmt* stmt) : stmt{stmt} {} +#endif }; /** @@ -9396,18 +10638,18 @@ namespace sqlite_orm { object_from_column_builder(object_type& object_, sqlite3_stmt* stmt_) : object_from_column_builder_base{stmt_}, object(object_) {} - template<class C> - void operator()(const C& c) const { - using field_type = typename C::field_type; - auto value = row_extractor<field_type>().extract(this->stmt, this->index++); - if(c.member_pointer) { - this->object.*c.member_pointer = std::move(value); - } else { - ((this->object).*(c.setter))(std::move(value)); - } + template<class G, class S> + void operator()(const column_field<G, S>& column) { + auto value = row_extractor<member_field_type_t<G>>().extract(this->stmt, this->index++); + static_if<std::is_member_object_pointer<G>::value>( + [&value, &object = this->object](const auto& column) { + object.*column.member_pointer = std::move(value); + }, + [&value, &object = this->object](const auto& column) { + (object.*column.setter)(std::move(value)); + })(column); } }; - } } @@ -9418,25 +10660,23 @@ namespace sqlite_orm { /** * This is a private row extractor class. It is used for extracting rows as objects instead of tuple. * Main difference from regular `row_extractor` is that this class takes table info which is required - * for constructing objects by member pointers. To construct please use `row_extractor_builder` class + * for constructing objects by member pointers. To construct please use `make_row_extractor()`. * Type arguments: * V is value type just like regular `row_extractor` has * T is table info class `table_t` */ - template<class V, class T> + template<class V, class Table> struct mapped_row_extractor { - using table_info_t = T; + using table_type = Table; - mapped_row_extractor(const table_info_t& tableInfo_) : tableInfo(tableInfo_) {} - - V extract(sqlite3_stmt* stmt, int /*columnIndex*/) { + V extract(sqlite3_stmt* stmt, int /*columnIndex*/) const { V res; object_from_column_builder<V> builder{res, stmt}; this->tableInfo.for_each_column(builder); return res; } - const table_info_t& tableInfo; + const table_type& tableInfo; }; } @@ -9447,37 +10687,15 @@ namespace sqlite_orm { namespace internal { - /** - * This builder is used to construct different row extractors depending on type. - * It has two specializations: for mapped to storage types (e.g. User, Visit etc) and - * for non-mapped (e.g. std::string, QString, int etc). For non mapped its operator() returns - * generic `row_extractor`, for mapped it returns `mapped_row_extractor` instance. - */ - template<class T, bool IsMapped, class I> - struct row_extractor_builder; - - template<class T, class I> - struct row_extractor_builder<T, false, I> { - - row_extractor<T> operator()(const I* /*tableInfo*/) const { - return {}; - } - }; - - template<class T, class I> - struct row_extractor_builder<T, true, I> { - - mapped_row_extractor<T, I> operator()(const I* tableInfo) const { - return {*tableInfo}; - } - }; - - template<class T, bool IsMapped, class I> - auto make_row_extractor(const I* tableInfo) { - using builder_t = row_extractor_builder<T, IsMapped, I>; - return builder_t{}(tableInfo); + template<class T> + row_extractor<T> make_row_extractor(nullptr_t) { + return {}; } + template<class T, class Table> + mapped_row_extractor<T, Table> make_row_extractor(const Table* table) { + return {*table}; + } } } @@ -9486,12 +10704,8 @@ namespace sqlite_orm { // #include "type_printer.h" -// #include "tuple_helper/tuple_helper.h" - // #include "constraints.h" -// #include "type_is_nullable.h" - // #include "field_printer.h" // #include "rowid.h" @@ -9518,35 +10732,28 @@ namespace sqlite_orm { // #include "journal_mode.h" -// #include "field_value_holder.h" - // #include "view.h" -#include <memory> // std::shared_ptr +#include <sqlite3.h> #include <string> // std::string #include <utility> // std::forward, std::move -#include <sqlite3.h> -#include <system_error> // std::system_error #include <tuple> // std::tuple, std::make_tuple // #include "row_extractor.h" -// #include "statement_finalizer.h" - // #include "error_code.h" // #include "iterator.h" -#include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared #include <sqlite3.h> +#include <memory> // std::shared_ptr, std::unique_ptr, std::make_shared #include <type_traits> // std::decay #include <utility> // std::move -#include <cstddef> // std::ptrdiff_t #include <iterator> // std::input_iterator_tag #include <system_error> // std::system_error -#include <ios> // std::make_error_code +#include <functional> // std::bind -// #include "row_extractor.h" +// #include "functional/cxx_universal.h" // #include "statement_finalizer.h" @@ -9554,6 +10761,10 @@ namespace sqlite_orm { // #include "object_from_column_builder.h" +// #include "storage_lookup.h" + +// #include "util.h" + namespace sqlite_orm { namespace internal { @@ -9565,15 +10776,13 @@ namespace sqlite_orm { protected: /** - * The double-indirection is so that copies of the iterator - * share the same sqlite3_stmt from a sqlite3_prepare_v2() - * call. When one finishes iterating it the pointer - * inside the shared_ptr is nulled out in all copies. + * shared_ptr is used over unique_ptr here + * so that the iterator can be copyable. */ - std::shared_ptr<statement_finalizer> stmt; + std::shared_ptr<sqlite3_stmt> stmt; // only null for the default constructed iterator - view_type* view; + view_type* view = nullptr; /** * shared_ptr is used over unique_ptr here @@ -9582,29 +10791,37 @@ namespace sqlite_orm { std::shared_ptr<value_type> current; void extract_value() { - auto& storage = this->view->storage; - auto& impl = storage.template get_impl<value_type>(); + auto& dbObjects = obtain_db_objects(this->view->storage); this->current = std::make_shared<value_type>(); - object_from_column_builder<value_type> builder{*this->current, this->stmt->get()}; - impl.table.for_each_column(builder); + object_from_column_builder<value_type> builder{*this->current, this->stmt.get()}; + pick_table<value_type>(dbObjects).for_each_column(builder); + } + + void next() { + this->current.reset(); + if(sqlite3_stmt* stmt = this->stmt.get()) { + perform_step(stmt, std::bind(&iterator_t::extract_value, this)); + if(!this->current) { + this->stmt.reset(); + } + } } public: - using difference_type = std::ptrdiff_t; + using difference_type = ptrdiff_t; using pointer = value_type*; using reference = value_type&; using iterator_category = std::input_iterator_tag; - iterator_t() : view(nullptr){}; + iterator_t(){}; - iterator_t(sqlite3_stmt* stmt_, view_type& view_) : - stmt(std::make_shared<statement_finalizer>(stmt_)), view(&view_) { + iterator_t(statement_finalizer stmt_, view_type& view_) : stmt{move(stmt_)}, view{&view_} { next(); } const value_type& operator*() const { if(!this->stmt || !this->current) { - throw std::system_error(std::make_error_code(orm_error_code::trying_to_dereference_null_iterator)); + throw std::system_error{orm_error_code::trying_to_dereference_null_iterator}; } return *this->current; } @@ -9613,29 +10830,6 @@ namespace sqlite_orm { return &(this->operator*()); } - private: - void next() { - this->current.reset(); - if(this->stmt) { - auto statementPointer = this->stmt->get(); - auto ret = sqlite3_step(statementPointer); - switch(ret) { - case SQLITE_ROW: - this->extract_value(); - break; - case SQLITE_DONE: - this->stmt.reset(); - break; - default: { - auto db = this->view->connection.get(); - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - } - - public: iterator_t<V>& operator++() { next(); return *this; @@ -9661,29 +10855,38 @@ namespace sqlite_orm { #include <vector> // std::vector #include <functional> // std::reference_wrapper +// #include "tuple_helper/tuple_iteration.h" + // #include "conditions.h" // #include "select_constraints.h" // #include "operators.h" -// #include "tuple_helper/tuple_helper.h" - // #include "core_functions.h" // #include "prepared_statement.h" #include <sqlite3.h> +#include <memory> // std::unique_ptr #include <iterator> // std::iterator_traits #include <string> // std::string -#include <type_traits> // std::true_type, std::false_type +#include <type_traits> // std::integral_constant, std::declval #include <utility> // std::pair +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "tuple_helper/tuple_filter.h" + // #include "connection_holder.h" #include <sqlite3.h> +#include <atomic> #include <string> // std::string -#include <system_error> // std::system_error // #include "error_code.h" @@ -9696,23 +10899,19 @@ namespace sqlite_orm { connection_holder(std::string filename_) : filename(move(filename_)) {} void retain() { - ++this->_retain_count; - if(1 == this->_retain_count) { + if(1 == ++this->_retain_count) { auto rc = sqlite3_open(this->filename.c_str(), &this->db); if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), - sqlite3_errmsg(this->db)); + throw_translated_sqlite_error(db); } } } void release() { - --this->_retain_count; - if(0 == this->_retain_count) { + if(0 == --this->_retain_count) { auto rc = sqlite3_close(this->db); if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(this->db), get_sqlite_error_category()), - sqlite3_errmsg(this->db)); + throw_translated_sqlite_error(db); } } } @@ -9729,7 +10928,7 @@ namespace sqlite_orm { protected: sqlite3* db = nullptr; - int _retain_count = 0; + std::atomic_int _retain_count{}; }; struct connection_ref { @@ -9764,9 +10963,12 @@ namespace sqlite_orm { // #include "values.h" #include <vector> // std::vector -#include <initializer_list> #include <tuple> // std::tuple -#include <type_traits> // std::false_type, std::true_type +#include <utility> // std::forward + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" namespace sqlite_orm { @@ -9780,10 +10982,10 @@ namespace sqlite_orm { }; template<class T> - struct is_values : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_values_v = polyfill::is_specialization_of_v<T, values_t>; - template<class... Args> - struct is_values<values_t<Args...>> : std::true_type {}; + template<class T> + using is_values = polyfill::bool_constant<is_values_v<T>>; template<class T> struct dynamic_values_t { @@ -9804,6 +11006,69 @@ namespace sqlite_orm { } +// #include "ast/upsert_clause.h" + +#include <tuple> // std::tuple, std::make_tuple +#include <type_traits> // std::false_type, std::true_type +#include <utility> // std::forward, std::move + +// #include "../functional/cxx_type_traits_polyfill.h" + +namespace sqlite_orm { + namespace internal { +#if SQLITE_VERSION_NUMBER >= 3024000 + template<class T, class A> + struct upsert_clause; + + template<class... Args> + struct conflict_target { + using args_tuple = std::tuple<Args...>; + + args_tuple args; + + upsert_clause<args_tuple, std::tuple<>> do_nothing() { + return {move(this->args), {}}; + } + + template<class... ActionsArgs> + upsert_clause<args_tuple, std::tuple<ActionsArgs...>> do_update(ActionsArgs... actions) { + return {move(this->args), {std::make_tuple(std::forward<ActionsArgs>(actions)...)}}; + } + }; + + template<class... TargetArgs, class... ActionsArgs> + struct upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>> { + using target_args_tuple = std::tuple<TargetArgs...>; + using actions_tuple = std::tuple<ActionsArgs...>; + + target_args_tuple target_args; + + actions_tuple actions; + }; + + template<class T> + using is_upsert_clause = polyfill::is_specialization_of<T, upsert_clause>; + } + + /** + * ON CONFLICT upsert clause builder function. + * @example + * storage.insert(into<Employee>(), + * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), + * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), + * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), + * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), + * c(&Employee::age) = excluded(&Employee::age), + * c(&Employee::address) = excluded(&Employee::address), + * c(&Employee::salary) = excluded(&Employee::salary)))); + */ + template<class... Args> + internal::conflict_target<Args...> on_conflict(Args... args) { + return {std::tuple<Args...>(std::forward<Args>(args)...)}; + } +#endif +} + namespace sqlite_orm { namespace internal { @@ -9812,20 +11077,19 @@ namespace sqlite_orm { sqlite3_stmt* stmt = nullptr; connection_ref con; +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + prepared_statement_base(sqlite3_stmt* stmt, connection_ref con) : stmt{stmt}, con{std::move(con)} {} +#endif + ~prepared_statement_base() { - if(this->stmt) { - sqlite3_finalize(this->stmt); - this->stmt = nullptr; - } + sqlite3_finalize(this->stmt); } std::string sql() const { - if(this->stmt) { - if(auto res = sqlite3_sql(this->stmt)) { - return res; - } else { - return {}; - } + // note: sqlite3 internally checks for null before calling + // sqlite3_normalized_sql() or sqlite3_expanded_sql(), so check here, too, even if superfluous + if(const char* sql = sqlite3_sql(this->stmt)) { + return sql; } else { return {}; } @@ -9833,14 +11097,10 @@ namespace sqlite_orm { #if SQLITE_VERSION_NUMBER >= 3014000 std::string expanded_sql() const { - if(this->stmt) { - if(auto res = sqlite3_expanded_sql(this->stmt)) { - std::string result = res; - sqlite3_free(res); - return result; - } else { - return {}; - } + // note: must check return value due to SQLITE_OMIT_TRACE + using char_ptr = std::unique_ptr<char, std::integral_constant<decltype(&sqlite3_free), sqlite3_free>>; + if(char_ptr sql{sqlite3_expanded_sql(this->stmt)}) { + return sql.get(); } else { return {}; } @@ -9848,34 +11108,43 @@ namespace sqlite_orm { #endif #if SQLITE_VERSION_NUMBER >= 3026000 and defined(SQLITE_ENABLE_NORMALIZE) std::string normalized_sql() const { - if(this->stmt) { - if(auto res = sqlite3_normalized_sql(this->stmt)) { - return res; - } else { - return {}; - } + if(const char* sql = sqlite3_normalized_sql(this->stmt)) { + return sql; } else { return {}; } } #endif + +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string_view column_name(int index) const { + return sqlite3_column_name(stmt, index); + } +#endif }; template<class T> struct prepared_statement_t : prepared_statement_base { using expression_type = T; - expression_type t; + expression_type expression; - prepared_statement_t(T t_, sqlite3_stmt* stmt_, connection_ref con_) : - prepared_statement_base{stmt_, std::move(con_)}, t(std::move(t_)) {} + prepared_statement_t(T expression_, sqlite3_stmt* stmt_, connection_ref con_) : + prepared_statement_base{stmt_, std::move(con_)}, expression(std::move(expression_)) {} + + prepared_statement_t(prepared_statement_t&& prepared_stmt) : + prepared_statement_base{prepared_stmt.stmt, std::move(prepared_stmt.con)}, + expression(std::move(prepared_stmt.expression)) { + prepared_stmt.stmt = nullptr; + } }; template<class T> - struct is_prepared_statement : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_prepared_statement_v = + polyfill::is_specialization_of_v<T, prepared_statement_t>; template<class T> - struct is_prepared_statement<prepared_statement_t<T>> : std::true_type {}; + using is_prepared_statement = polyfill::bool_constant<is_prepared_statement_v<T>>; /** * T - type of object to obtain from a database @@ -9962,7 +11231,7 @@ namespace sqlite_orm { struct update_t { using type = T; - type obj; + type object; }; template<class T, class... Ids> @@ -9977,14 +11246,14 @@ namespace sqlite_orm { struct insert_t { using type = T; - type obj; + type object; }; template<class T> - struct is_insert : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_v = polyfill::is_specialization_of_v<T, insert_t>; template<class T> - struct is_insert<insert_t<T>> : std::true_type {}; + using is_insert = polyfill::bool_constant<is_insert_v<T>>; template<class T, class... Cols> struct insert_explicit { @@ -9999,20 +11268,19 @@ namespace sqlite_orm { struct replace_t { using type = T; - type obj; + type object; }; template<class T> - struct is_replace : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_v = polyfill::is_specialization_of_v<T, replace_t>; template<class T> - struct is_replace<replace_t<T>> : std::true_type {}; + using is_replace = polyfill::bool_constant<is_replace_v<T>>; - template<class It, class L, class O> + template<class It, class Projection, class O> struct insert_range_t { using iterator_type = It; - using container_object_type = typename std::iterator_traits<iterator_type>::value_type; - using transformer_type = L; + using transformer_type = Projection; using object_type = O; std::pair<iterator_type, iterator_type> range; @@ -10020,16 +11288,15 @@ namespace sqlite_orm { }; template<class T> - struct is_insert_range : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_range_v = polyfill::is_specialization_of_v<T, insert_range_t>; - template<class It, class L, class O> - struct is_insert_range<insert_range_t<It, L, O>> : std::true_type {}; + template<class T> + using is_insert_range = polyfill::bool_constant<is_insert_range_v<T>>; - template<class It, class L, class O> + template<class It, class Projection, class O> struct replace_range_t { using iterator_type = It; - using container_object_type = typename std::iterator_traits<iterator_type>::value_type; - using transformer_type = L; + using transformer_type = Projection; using object_type = O; std::pair<iterator_type, iterator_type> range; @@ -10037,10 +11304,10 @@ namespace sqlite_orm { }; template<class T> - struct is_replace_range : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_range_v = polyfill::is_specialization_of_v<T, replace_range_t>; - template<class It, class L, class O> - struct is_replace_range<replace_range_t<It, L, O>> : std::true_type {}; + template<class T> + using is_replace_range = polyfill::bool_constant<is_replace_range_v<T>>; template<class... Args> struct insert_raw_t { @@ -10050,10 +11317,10 @@ namespace sqlite_orm { }; template<class T> - struct is_insert_raw : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_insert_raw_v = polyfill::is_specialization_of_v<T, insert_raw_t>; - template<class... Args> - struct is_insert_raw<insert_raw_t<Args...>> : std::true_type {}; + template<class T> + using is_insert_raw = polyfill::bool_constant<is_insert_raw_v<T>>; template<class... Args> struct replace_raw_t { @@ -10063,36 +11330,17 @@ namespace sqlite_orm { }; template<class T> - struct is_replace_raw : std::false_type {}; - - template<class... Args> - struct is_replace_raw<replace_raw_t<Args...>> : std::true_type {}; - - template<class T> - struct into_t { - using type = T; - }; - - template<class T> - struct is_into : std::false_type {}; + SQLITE_ORM_INLINE_VAR constexpr bool is_replace_raw_v = polyfill::is_specialization_of_v<T, replace_raw_t>; template<class T> - struct is_into<into_t<T>> : std::true_type {}; - - struct default_transformer { - - template<class T> - const T& operator()(const T& object) const { - return object; - } - }; + using is_replace_raw = polyfill::bool_constant<is_replace_raw_v<T>>; struct default_values_t {}; template<class T> - using is_default_values = std::is_same<default_values_t, T>; + using is_default_values = std::is_same<T, default_values_t>; - enum class insert_constraint { + enum class conflict_action { abort, fail, ignore, @@ -10100,31 +11348,36 @@ namespace sqlite_orm { rollback, }; - template<class T> - using is_insert_constraint = std::is_same<insert_constraint, T>; + struct insert_constraint { + conflict_action action = conflict_action::abort; + +#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED + insert_constraint(conflict_action action) : action{action} {} +#endif + }; template<class T> - struct is_upsert_clause; + using is_insert_constraint = std::is_same<T, insert_constraint>; } inline internal::insert_constraint or_rollback() { - return internal::insert_constraint::rollback; + return {internal::conflict_action::rollback}; } inline internal::insert_constraint or_replace() { - return internal::insert_constraint::replace; + return {internal::conflict_action::replace}; } inline internal::insert_constraint or_ignore() { - return internal::insert_constraint::ignore; + return {internal::conflict_action::ignore}; } inline internal::insert_constraint or_fail() { - return internal::insert_constraint::fail; + return {internal::conflict_action::fail}; } inline internal::insert_constraint or_abort() { - return internal::insert_constraint::abort; + return {internal::conflict_action::abort}; } /** @@ -10139,11 +11392,6 @@ namespace sqlite_orm { return {}; } - template<class T> - internal::into_t<T> into() { - return {}; - } - /** * Raw insert statement creation routine. Use this if `insert` with object does not fit you. This insert is designed to be able * to call any type of `INSERT` query with no limitations. @@ -10286,7 +11534,8 @@ namespace sqlite_orm { } /** - * Create a replace range statement + * Create a replace range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. * * @example * ``` @@ -10295,33 +11544,33 @@ namespace sqlite_orm { * auto statement = storage.prepare(replace_range(users.begin(), users.end())); * storage.execute(statement); * ``` - */ - template<class It> - internal::replace_range_t<It, internal::default_transformer, typename std::iterator_traits<It>::value_type> - replace_range(It from, It to) { - return {{std::move(from), std::move(to)}}; - } - - /** - * Create an replace range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, - * optionals or whatever. * @example * ``` * std::vector<std::unique_ptr<User>> userPointers; * userPointers.push_back(std::make_unique<User>(1, "Eneli")); - * auto statement = storage.prepare(replace_range<User>(userPointers.begin(), userPointers.end(), [](const std::unique_ptr<User> &userPointer) -> const User & { - * return *userPointer; - * })); + * auto statement = storage.prepare(replace_range(userPointers.begin(), userPointers.end(), &std::unique_ptr<User>::operator*)); * storage.execute(statement); * ``` */ - template<class T, class It, class L> - internal::replace_range_t<It, L, T> replace_range(It from, It to, L transformer) { - return {{std::move(from), std::move(to)}, std::move(transformer)}; + template<class It, class Projection = polyfill::identity> + auto replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t<decltype(polyfill::invoke(std::declval<Projection>(), *std::declval<It>()))>; + return internal::replace_range_t<It, Projection, O>{{std::move(from), std::move(to)}, std::move(project)}; + } + + /* + * Create a replace range statement. + * Overload of `replace_range(It, It, Projection)` with explicit object type template parameter. + */ + template<class O, class It, class Projection = polyfill::identity> + internal::replace_range_t<It, Projection, O> replace_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; } /** - * Create an insert range statement + * Create an insert range statement. + * The objects in the range are transformed using the specified projection, which defaults to identity projection. + * * @example * ``` * std::vector<User> users; @@ -10329,30 +11578,29 @@ namespace sqlite_orm { * auto statement = storage.prepare(insert_range(users.begin(), users.end())); * storage.execute(statement); * ``` - */ - template<class It> - internal::insert_range_t<It, internal::default_transformer, typename std::iterator_traits<It>::value_type> - insert_range(It from, It to) { - return {{std::move(from), std::move(to)}, internal::default_transformer{}}; - } - - /** - * Create an insert range statement with explicit transformer. Transformer is used to apply containers with no strict objects with other kind of objects like pointers, - * optionals or whatever. * @example * ``` * std::vector<std::unique_ptr<User>> userPointers; * userPointers.push_back(std::make_unique<User>(1, "Eneli")); - * auto statement = storage.prepare(insert_range<User>(userPointers.begin(), userPointers.end(), [](const std::unique_ptr<User> &userPointer) -> const User & { - * return *userPointer; - * })); + * auto statement = storage.prepare(insert_range(userPointers.begin(), userPointers.end(), &std::unique_ptr<User>::operator*)); * storage.execute(statement); * ``` */ - template<class T, class It, class L> - internal::insert_range_t<It, L, T> insert_range(It from, It to, L transformer) { - return {{std::move(from), std::move(to)}, std::move(transformer)}; + template<class It, class Projection = polyfill::identity> + auto insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t<decltype(polyfill::invoke(std::declval<Projection>(), *std::declval<It>()))>; + return internal::insert_range_t<It, Projection, O>{{std::move(from), std::move(to)}, std::move(project)}; } + + /* + * Create an insert range statement. + * Overload of `insert_range(It, It, Projection)` with explicit object type template parameter. + */ + template<class O, class It, class Projection = polyfill::identity> + internal::insert_range_t<It, Projection, O> insert_range(It from, It to, Projection project = {}) { + return {{std::move(from), std::move(to)}, std::move(project)}; + } + /** * Create a replace statement. * T is an object type mapped to a storage. @@ -10562,6 +11810,8 @@ namespace sqlite_orm { // #include "ast/excluded.h" +#include <utility> // std::move + namespace sqlite_orm { namespace internal { @@ -10581,68 +11831,46 @@ namespace sqlite_orm { // #include "ast/upsert_clause.h" -#include <tuple> // std::tuple -#include <type_traits> // std::false_type, std::true_type +// #include "ast/where.h" -namespace sqlite_orm { - namespace internal { +// #include "ast/into.h" - template<class T, class A> - struct upsert_clause; +// #include "ast/group_by.h" - template<class... Args> - struct conflict_target { - using args_tuple = std::tuple<Args...>; +// #include "ast/exists.h" - args_tuple args; +#include <utility> // std::move - upsert_clause<args_tuple, std::tuple<>> do_nothing() { - return {std::move(this->args), {}}; - } +// #include "../tags.h" - template<class... ActionsArgs> - upsert_clause<args_tuple, std::tuple<ActionsArgs...>> do_update(ActionsArgs... actions) { - return {std::move(this->args), {std::make_tuple(std::forward<ActionsArgs>(actions)...)}}; - } - }; +namespace sqlite_orm { + namespace internal { - template<class... TargetArgs, class... ActionsArgs> - struct upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>> { - using target_args_tuple = std::tuple<TargetArgs...>; - using actions_tuple = std::tuple<ActionsArgs...>; + template<class T> + struct exists_t : condition_t, negatable_t { + using expression_type = T; + using self = exists_t<expression_type>; - target_args_tuple target_args; + expression_type expression; - actions_tuple actions; + exists_t(expression_type expression_) : expression(std::move(expression_)) {} }; - - template<class T> - struct is_upsert_clause : std::false_type {}; - - template<class T, class A> - struct is_upsert_clause<upsert_clause<T, A>> : std::true_type {}; } /** - * ON CONFLICT upsert clause builder function. - * @example - * storage.insert(into<Employee>(), - * columns(&Employee::id, &Employee::name, &Employee::age, &Employee::address, &Employee::salary), - * values(std::make_tuple(3, "Sofia", 26, "Madrid", 15000.0), - * std::make_tuple(4, "Doja", 26, "LA", 25000.0)), - * on_conflict(&Employee::id).do_update(set(c(&Employee::name) = excluded(&Employee::name), - * c(&Employee::age) = excluded(&Employee::age), - * c(&Employee::address) = excluded(&Employee::address), - * c(&Employee::salary) = excluded(&Employee::salary)))); + * EXISTS(condition). + * Example: storage.select(columns(&Agent::code, &Agent::name, &Agent::workingArea, &Agent::comission), + where(exists(select(asterisk<Customer>(), + where(is_equal(&Customer::grade, 3) and + is_equal(&Agent::code, &Customer::agentCode))))), + order_by(&Agent::comission)); */ - template<class... Args> - internal::conflict_target<Args...> on_conflict(Args... args) { - return {std::tuple<Args...>(std::forward<Args>(args)...)}; + template<class T> + internal::exists_t<T> exists(T expression) { + return {std::move(expression)}; } } -// #include "ast/where.h" - namespace sqlite_orm { namespace internal { @@ -10664,8 +11892,8 @@ namespace sqlite_orm { * L is a callable type. Mostly is a templated lambda */ template<class L> - void operator()(const T& t, const L& l) const { - l(t); + void operator()(const T& t, L& lambda) const { + lambda(t); } }; @@ -10673,9 +11901,9 @@ namespace sqlite_orm { * Simplified API */ template<class T, class L> - void iterate_ast(const T& t, const L& l) { + void iterate_ast(const T& t, L&& lambda) { ast_iterator<T> iterator; - iterator(t, l); + iterator(t, lambda); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -10684,7 +11912,7 @@ namespace sqlite_orm { using node_type = as_optional_t<T>; template<class L> - void operator()(const node_type& node, const L& lambda) const { + void operator()(const node_type& node, L& lambda) const { iterate_ast(node.value, lambda); } }; @@ -10695,8 +11923,18 @@ namespace sqlite_orm { using node_type = std::reference_wrapper<T>; template<class L> - void operator()(const node_type& r, const L& lambda) const { - iterate_ast(r.get(), lambda); + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.get(), lambda); + } + }; + + template<class... Args> + struct ast_iterator<group_by_t<Args...>, void> { + using node_type = group_by_t<Args...>; + + template<class L> + void operator()(const node_type& expression, L& lambda) const { + iterate_ast(expression.args, lambda); } }; @@ -10705,7 +11943,7 @@ namespace sqlite_orm { using node_type = excluded_t<T>; template<class L> - void operator()(const node_type& expression, const L& lambda) const { + void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.expression, lambda); } }; @@ -10715,7 +11953,7 @@ namespace sqlite_orm { using node_type = upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>; template<class L> - void operator()(const node_type& expression, const L& lambda) const { + void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.actions, lambda); } }; @@ -10725,19 +11963,19 @@ namespace sqlite_orm { using node_type = where_t<C>; template<class L> - void operator()(const node_type& expression, const L& lambda) const { + void operator()(const node_type& expression, L& lambda) const { iterate_ast(expression.expression, lambda); } }; template<class T> - struct ast_iterator<T, typename std::enable_if<is_base_of_template<T, binary_condition>::value>::type> { + struct ast_iterator<T, std::enable_if_t<is_base_of_template_v<T, binary_condition>>> { using node_type = T; template<class L> - void operator()(const node_type& binaryCondition, const L& l) const { - iterate_ast(binaryCondition.l, l); - iterate_ast(binaryCondition.r, l); + void operator()(const node_type& binaryCondition, L& lambda) const { + iterate_ast(binaryCondition.l, lambda); + iterate_ast(binaryCondition.r, lambda); } }; @@ -10746,9 +11984,9 @@ namespace sqlite_orm { using node_type = binary_operator<L, R, Ds...>; template<class C> - void operator()(const node_type& binaryOperator, const C& l) const { - iterate_ast(binaryOperator.lhs, l); - iterate_ast(binaryOperator.rhs, l); + void operator()(const node_type& binaryOperator, C& lambda) const { + iterate_ast(binaryOperator.lhs, lambda); + iterate_ast(binaryOperator.rhs, lambda); } }; @@ -10757,8 +11995,8 @@ namespace sqlite_orm { using node_type = columns_t<Args...>; template<class L> - void operator()(const node_type& cols, const L& l) const { - iterate_ast(cols.columns, l); + void operator()(const node_type& cols, L& lambda) const { + iterate_ast(cols.columns, lambda); } }; @@ -10767,9 +12005,9 @@ namespace sqlite_orm { using node_type = dynamic_in_t<L, A>; template<class C> - void operator()(const node_type& in, const C& l) const { - iterate_ast(in.left, l); - iterate_ast(in.argument, l); + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); } }; @@ -10778,9 +12016,9 @@ namespace sqlite_orm { using node_type = in_t<L, Args...>; template<class C> - void operator()(const node_type& in, const C& l) const { - iterate_ast(in.left, l); - iterate_ast(in.argument, l); + void operator()(const node_type& in, C& lambda) const { + iterate_ast(in.left, lambda); + iterate_ast(in.argument, lambda); } }; @@ -10789,9 +12027,9 @@ namespace sqlite_orm { using node_type = std::vector<T>; template<class L> - void operator()(const node_type& vec, const L& l) const { + void operator()(const node_type& vec, L& lambda) const { for(auto& i: vec) { - iterate_ast(i, l); + iterate_ast(i, lambda); } } }; @@ -10801,19 +12039,19 @@ namespace sqlite_orm { using node_type = std::vector<char>; template<class L> - void operator()(const node_type& vec, const L& l) const { - l(vec); + void operator()(const node_type& vec, L& lambda) const { + lambda(vec); } }; template<class T> - struct ast_iterator<T, typename std::enable_if<is_base_of_template<T, compound_operator>::value>::type> { + struct ast_iterator<T, std::enable_if_t<is_base_of_template_v<T, compound_operator>>> { using node_type = T; template<class L> - void operator()(const node_type& c, const L& l) const { - iterate_ast(c.left, l); - iterate_ast(c.right, l); + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.left, lambda); + iterate_ast(c.right, lambda); } }; @@ -10822,7 +12060,7 @@ namespace sqlite_orm { using node_type = into_t<T>; template<class L> - void operator()(const node_type& node, const L& l) const { + void operator()(const node_type& /*node*/, L& /*lambda*/) const { //.. } }; @@ -10832,8 +12070,8 @@ namespace sqlite_orm { using node_type = insert_raw_t<Args...>; template<class L> - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.args, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); } }; @@ -10842,8 +12080,8 @@ namespace sqlite_orm { using node_type = replace_raw_t<Args...>; template<class L> - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.args, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); } }; @@ -10852,9 +12090,9 @@ namespace sqlite_orm { using node_type = select_t<T, Args...>; template<class L> - void operator()(const node_type& sel, const L& l) const { - iterate_ast(sel.col, l); - iterate_ast(sel.conditions, l); + void operator()(const node_type& sel, L& lambda) const { + iterate_ast(sel.col, lambda); + iterate_ast(sel.conditions, lambda); } }; @@ -10863,8 +12101,8 @@ namespace sqlite_orm { using node_type = get_all_t<T, R, Args...>; template<class L> - void operator()(const node_type& get, const L& l) const { - iterate_ast(get.conditions, l); + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); } }; @@ -10873,8 +12111,8 @@ namespace sqlite_orm { using node_type = get_all_pointer_t<T, Args...>; template<class L> - void operator()(const node_type& get, const L& l) const { - iterate_ast(get.conditions, l); + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); } }; @@ -10884,8 +12122,8 @@ namespace sqlite_orm { using node_type = get_all_optional_t<T, Args...>; template<class L> - void operator()(const node_type& get, const L& l) const { - iterate_ast(get.conditions, l); + void operator()(const node_type& get, L& lambda) const { + iterate_ast(get.conditions, lambda); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED @@ -10895,9 +12133,9 @@ namespace sqlite_orm { using node_type = update_all_t<set_t<Args...>, Wargs...>; template<class L> - void operator()(const node_type& u, const L& l) const { - iterate_ast(u.set, l); - iterate_ast(u.conditions, l); + void operator()(const node_type& u, L& lambda) const { + iterate_ast(u.set, lambda); + iterate_ast(u.conditions, lambda); } }; @@ -10906,8 +12144,8 @@ namespace sqlite_orm { using node_type = remove_all_t<T, Args...>; template<class L> - void operator()(const node_type& r, const L& l) const { - iterate_ast(r.conditions, l); + void operator()(const node_type& r, L& lambda) const { + iterate_ast(r.conditions, lambda); } }; @@ -10916,8 +12154,8 @@ namespace sqlite_orm { using node_type = set_t<Args...>; template<class L> - void operator()(const node_type& s, const L& l) const { - iterate_ast(s.assigns, l); + void operator()(const node_type& s, L& lambda) const { + iterate_ast(s.assigns, lambda); } }; @@ -10926,20 +12164,31 @@ namespace sqlite_orm { using node_type = std::tuple<Args...>; template<class L> - void operator()(const node_type& tuple, const L& l) const { - iterate_tuple(tuple, [&l](auto& v) { - iterate_ast(v, l); + void operator()(const node_type& node, L& lambda) const { + iterate_tuple(node, [&lambda](auto& v) { + iterate_ast(v, lambda); }); } }; + template<class T, class... Args> + struct ast_iterator<group_by_with_having<T, Args...>, void> { + using node_type = group_by_with_having<T, Args...>; + + template<class L> + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + iterate_ast(node.expression, lambda); + } + }; + template<class T> struct ast_iterator<having_t<T>, void> { using node_type = having_t<T>; template<class L> - void operator()(const node_type& hav, const L& l) const { - iterate_ast(hav.t, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); } }; @@ -10948,8 +12197,8 @@ namespace sqlite_orm { using node_type = cast_t<T, E>; template<class L> - void operator()(const node_type& c, const L& l) const { - iterate_ast(c.expression, l); + void operator()(const node_type& c, L& lambda) const { + iterate_ast(c.expression, lambda); } }; @@ -10958,8 +12207,8 @@ namespace sqlite_orm { using node_type = exists_t<T>; template<class L> - void operator()(const node_type& e, const L& l) const { - iterate_ast(e.t, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); } }; @@ -10968,11 +12217,11 @@ namespace sqlite_orm { using node_type = like_t<A, T, E>; template<class L> - void operator()(const node_type& lk, const L& l) const { - iterate_ast(lk.arg, l); - iterate_ast(lk.pattern, l); - lk.arg3.apply([&l](auto& value) { - iterate_ast(value, l); + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); + lk.arg3.apply([&lambda](auto& value) { + iterate_ast(value, lambda); }); } }; @@ -10982,9 +12231,9 @@ namespace sqlite_orm { using node_type = glob_t<A, T>; template<class L> - void operator()(const node_type& lk, const L& l) const { - iterate_ast(lk.arg, l); - iterate_ast(lk.pattern, l); + void operator()(const node_type& lk, L& lambda) const { + iterate_ast(lk.arg, lambda); + iterate_ast(lk.pattern, lambda); } }; @@ -10993,10 +12242,10 @@ namespace sqlite_orm { using node_type = between_t<A, T>; template<class L> - void operator()(const node_type& b, const L& l) const { - iterate_ast(b.expr, l); - iterate_ast(b.b1, l); - iterate_ast(b.b2, l); + void operator()(const node_type& b, L& lambda) const { + iterate_ast(b.expr, lambda); + iterate_ast(b.b1, lambda); + iterate_ast(b.b2, lambda); } }; @@ -11005,8 +12254,8 @@ namespace sqlite_orm { using node_type = named_collate<T>; template<class L> - void operator()(const node_type& col, const L& l) const { - iterate_ast(col.expr, l); + void operator()(const node_type& col, L& lambda) const { + iterate_ast(col.expr, lambda); } }; @@ -11015,8 +12264,8 @@ namespace sqlite_orm { using node_type = negated_condition_t<C>; template<class L> - void operator()(const node_type& neg, const L& l) const { - iterate_ast(neg.c, l); + void operator()(const node_type& neg, L& lambda) const { + iterate_ast(neg.c, lambda); } }; @@ -11025,8 +12274,8 @@ namespace sqlite_orm { using node_type = is_null_t<T>; template<class L> - void operator()(const node_type& i, const L& l) const { - iterate_ast(i.t, l); + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); } }; @@ -11035,8 +12284,8 @@ namespace sqlite_orm { using node_type = is_not_null_t<T>; template<class L> - void operator()(const node_type& i, const L& l) const { - iterate_ast(i.t, l); + void operator()(const node_type& i, L& lambda) const { + iterate_ast(i.t, lambda); } }; @@ -11045,8 +12294,8 @@ namespace sqlite_orm { using node_type = function_call<F, Args...>; template<class L> - void operator()(const node_type& f, const L& l) const { - iterate_ast(f.args, l); + void operator()(const node_type& f, L& lambda) const { + iterate_ast(f.args, lambda); } }; @@ -11055,8 +12304,29 @@ namespace sqlite_orm { using node_type = built_in_function_t<R, S, Args...>; template<class L> - void operator()(const node_type& f, const L& l) const { - iterate_ast(f.args, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template<class R, class S, class... Args> + struct ast_iterator<built_in_aggregate_function_t<R, S, Args...>, void> { + using node_type = built_in_aggregate_function_t<R, S, Args...>; + + template<class L> + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.args, lambda); + } + }; + + template<class F, class W> + struct ast_iterator<filtered_aggregate_function<F, W>, void> { + using node_type = filtered_aggregate_function<F, W>; + + template<class L> + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.function, lambda); + iterate_ast(node.where, lambda); } }; @@ -11065,8 +12335,8 @@ namespace sqlite_orm { using node_type = left_join_t<T, O>; template<class L> - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11075,8 +12345,20 @@ namespace sqlite_orm { using node_type = on_t<T>; template<class L> - void operator()(const node_type& o, const L& l) const { - iterate_ast(o.arg, l); + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.arg, lambda); + } + }; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template<class T> + struct ast_iterator<T, std::enable_if_t<polyfill::is_specialization_of_v<T, using_t>>> { + using node_type = T; + + template<class L> + void operator()(const node_type& o, L& lambda) const { + iterate_ast(o.column, lambda); } }; @@ -11085,8 +12367,8 @@ namespace sqlite_orm { using node_type = join_t<T, O>; template<class L> - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11095,8 +12377,8 @@ namespace sqlite_orm { using node_type = left_outer_join_t<T, O>; template<class L> - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11105,8 +12387,8 @@ namespace sqlite_orm { using node_type = inner_join_t<T, O>; template<class L> - void operator()(const node_type& j, const L& l) const { - iterate_ast(j.constraint, l); + void operator()(const node_type& j, L& lambda) const { + iterate_ast(j.constraint, lambda); } }; @@ -11115,16 +12397,16 @@ namespace sqlite_orm { using node_type = simple_case_t<R, T, E, Args...>; template<class L> - void operator()(const node_type& c, const L& l) const { - c.case_expression.apply([&l](auto& c_) { - iterate_ast(c_, l); + void operator()(const node_type& c, L& lambda) const { + c.case_expression.apply([&lambda](auto& c_) { + iterate_ast(c_, lambda); }); - iterate_tuple(c.args, [&l](auto& pair) { - iterate_ast(pair.first, l); - iterate_ast(pair.second, l); + iterate_tuple(c.args, [&lambda](auto& pair) { + iterate_ast(pair.first, lambda); + iterate_ast(pair.second, lambda); }); - c.else_expression.apply([&l](auto& el) { - iterate_ast(el, l); + c.else_expression.apply([&lambda](auto& el) { + iterate_ast(el, lambda); }); } }; @@ -11134,8 +12416,8 @@ namespace sqlite_orm { using node_type = as_t<T, E>; template<class L> - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.expression, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.expression, lambda); } }; @@ -11144,8 +12426,8 @@ namespace sqlite_orm { using node_type = limit_t<T, false, OI, void>; template<class L> - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.lim, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); } }; @@ -11154,10 +12436,10 @@ namespace sqlite_orm { using node_type = limit_t<T, true, false, O>; template<class L> - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.lim, l); - a.off.apply([&l](auto& value) { - iterate_ast(value, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.lim, lambda); + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); }); } }; @@ -11167,11 +12449,11 @@ namespace sqlite_orm { using node_type = limit_t<T, true, true, O>; template<class L> - void operator()(const node_type& a, const L& l) const { - a.off.apply([&l](auto& value) { - iterate_ast(value, l); + void operator()(const node_type& a, L& lambda) const { + a.off.apply([&lambda](auto& value) { + iterate_ast(value, lambda); }); - iterate_ast(a.lim, l); + iterate_ast(a.lim, lambda); } }; @@ -11180,8 +12462,8 @@ namespace sqlite_orm { using node_type = distinct_t<T>; template<class L> - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.value, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); } }; @@ -11190,8 +12472,8 @@ namespace sqlite_orm { using node_type = all_t<T>; template<class L> - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.value, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.value, lambda); } }; @@ -11200,8 +12482,8 @@ namespace sqlite_orm { using node_type = bitwise_not_t<T>; template<class L> - void operator()(const node_type& a, const L& l) const { - iterate_ast(a.argument, l); + void operator()(const node_type& a, L& lambda) const { + iterate_ast(a.argument, lambda); } }; @@ -11210,8 +12492,8 @@ namespace sqlite_orm { using node_type = values_t<Args...>; template<class L> - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.tuple, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.tuple, lambda); } }; @@ -11220,8 +12502,32 @@ namespace sqlite_orm { using node_type = dynamic_values_t<T>; template<class L> - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.vector, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.vector, lambda); + } + }; + + /** + * Column alias or literal + */ + template<class T> + struct ast_iterator< + T, + std::enable_if_t<polyfill::disjunction_v<polyfill::is_specialization_of<T, alias_holder>, + polyfill::is_specialization_of<T, literal_holder>>>> { + using node_type = T; + + template<class L> + void operator()(const node_type& /*node*/, L& /*lambda*/) const {} + }; + + template<class E> + struct ast_iterator<order_by_t<E>, void> { + using node_type = order_by_t<E>; + + template<class L> + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expression, lambda); } }; @@ -11230,8 +12536,8 @@ namespace sqlite_orm { using node_type = collate_t<T>; template<class L> - void operator()(const node_type& node, const L& l) const { - iterate_ast(node.expr, l); + void operator()(const node_type& node, L& lambda) const { + iterate_ast(node.expr, lambda); } }; @@ -11242,6 +12548,8 @@ namespace sqlite_orm { // #include "connection_holder.h" +// #include "util.h" + namespace sqlite_orm { namespace internal { @@ -11277,29 +12585,14 @@ namespace sqlite_orm { } iterator_t<self> begin() { - sqlite3_stmt* stmt = nullptr; - auto db = this->connection.get(); - using context_t = serializator_context<typename storage_type::impl_type>; - context_t context{this->storage.impl}; + using context_t = serializer_context<typename storage_type::db_objects_type>; + context_t context{obtain_db_objects(this->storage)}; context.skip_table_name = false; context.replace_bindable_with_question = true; - auto query = serialize(this->args, context); - auto ret = sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr); - if(ret == SQLITE_OK) { - auto index = 1; - iterate_ast(this->args.conditions, [&index, stmt, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - return {stmt, *this}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + + statement_finalizer stmt{prepare_stmt(this->connection.get(), serialize(this->args, context))}; + iterate_ast(this->args.conditions, conditional_binder{stmt.get()}); + return {move(stmt), *this}; } iterator_t<self> end() { @@ -11313,48 +12606,477 @@ namespace sqlite_orm { // #include "storage_base.h" -#include <functional> // std::function, std::bind #include <sqlite3.h> +#include <functional> // std::function, std::bind #include <string> // std::string #include <sstream> // std::stringstream #include <utility> // std::move -#include <system_error> // std::system_error, std::error_code, std::make_error_code +#include <system_error> // std::system_error #include <vector> // std::vector -#include <memory> // std::make_shared, std::shared_ptr +#include <memory> // std::make_unique, std::unique_ptr #include <map> // std::map #include <type_traits> // std::decay, std::is_same -#include <algorithm> // std::iter_swap +#include <algorithm> // std::find_if + +// #include "functional/cxx_universal.h" + +// #include "functional/static_magic.h" + +// #include "tuple_helper/tuple_iteration.h" // #include "pragma.h" -#include <string> // std::string #include <sqlite3.h> +#include <string> // std::string #include <functional> // std::function #include <memory> // std::shared_ptr #include <vector> // std::vector +#include <sstream> // #include "error_code.h" -// #include "util.h" - // #include "row_extractor.h" // #include "journal_mode.h" // #include "connection_holder.h" +// #include "util.h" + +// #include "serializing_util.h" + +#include <type_traits> // std::index_sequence +#include <tuple> +#include <array> +#include <string> +#include <ostream> +#include <utility> // std::exchange, std::tuple_size +#if __cplusplus >= 202002L && __cpp_lib_concepts +#include <string_view> +#include <algorithm> // std::find +#endif + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "tuple_helper/tuple_iteration.h" + +// #include "error_code.h" + +// #include "serializer_context.h" + +// #include "util.h" + +namespace sqlite_orm { + namespace internal { + template<class O> + struct order_by_t; + + template<class T, class I> + std::string serialize(const T& t, const serializer_context<I>& context); + + template<class T, class Ctx> + std::string serialize_order_by(const T& t, const Ctx& context); + +#if __cplusplus >= 202002L && \ + __cpp_lib_concepts // contiguous iterator ranges depend on contiguous_iterator, sized_sentinel_for in all major implementations + inline void stream_sql_escaped(std::ostream& os, const std::string& str, char char2Escape) { + for(std::string::const_iterator it = str.cbegin(), next; true; it = next + 1) { + next = std::find(it, str.cend(), char2Escape); + os << std::string_view{it, next}; + + if(next == str.cend()) [[likely]] { + break; + } + os << std::string(2, char2Escape); + } + } +#else + inline void stream_sql_escaped(std::ostream& os, const std::string& str, char char2Escape) { + if(str.find(char2Escape) == str.npos) { + os << str; + } else { + for(char c: str) { + if(c == char2Escape) { + os << char2Escape; + } + os << c; + } + } + } +#endif + + inline void stream_identifier(std::ostream& ss, + const std::string& qualifier, + const std::string& identifier, + const std::string& alias) { + constexpr char quoteChar = '"'; + constexpr char qualified[] = {quoteChar, '.', '\0'}; + constexpr char aliased[] = {' ', quoteChar, '\0'}; + + // note: In practice, escaping double quotes in identifiers is arguably overkill, + // but since the SQLite grammar allows it, it's better to be safe than sorry. + + if(!qualifier.empty()) { + ss << quoteChar; + stream_sql_escaped(ss, qualifier, quoteChar); + ss << qualified; + } + { + ss << quoteChar; + stream_sql_escaped(ss, identifier, quoteChar); + ss << quoteChar; + } + if(!alias.empty()) { + ss << aliased; + stream_sql_escaped(ss, alias, quoteChar); + ss << quoteChar; + } + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier, const std::string& alias) { + return stream_identifier(ss, std::string{}, identifier, alias); + } + + inline void stream_identifier(std::ostream& ss, const std::string& identifier) { + return stream_identifier(ss, std::string{}, identifier, std::string{}); + } + + template<typename Tpl, size_t... Is> + void stream_identifier(std::ostream& ss, const Tpl& tpl, std::index_sequence<Is...>) { + static_assert(sizeof...(Is) > 0 && sizeof...(Is) <= 3, ""); + return stream_identifier(ss, std::get<Is>(tpl)...); + } + + template<typename Tpl, std::enable_if_t<polyfill::is_detected_v<type_t, std::tuple_size<Tpl>>, bool> = true> + void stream_identifier(std::ostream& ss, const Tpl& tpl) { + return stream_identifier(ss, tpl, std::make_index_sequence<std::tuple_size<Tpl>::value>{}); + } + + enum class stream_as { + conditions_tuple, + actions_tuple, + expressions_tuple, + dynamic_expressions, + serialized, + identifier, + identifiers, + values_placeholders, + table_columns, + non_generated_columns, + field_values_excluding, + mapped_columns_expressions, + column_constraints, + }; + + template<stream_as mode> + struct streaming { + template<class... Ts> + auto operator()(const Ts&... ts) const { + return std::forward_as_tuple(*this, ts...); + } + + template<size_t... Idx> + constexpr std::index_sequence<1u + Idx...> offset_index(std::index_sequence<Idx...>) const { + return {}; + } + }; + constexpr streaming<stream_as::conditions_tuple> streaming_conditions_tuple{}; + constexpr streaming<stream_as::actions_tuple> streaming_actions_tuple{}; + constexpr streaming<stream_as::expressions_tuple> streaming_expressions_tuple{}; + constexpr streaming<stream_as::dynamic_expressions> streaming_dynamic_expressions{}; + constexpr streaming<stream_as::serialized> streaming_serialized{}; + constexpr streaming<stream_as::identifier> streaming_identifier{}; + constexpr streaming<stream_as::identifiers> streaming_identifiers{}; + constexpr streaming<stream_as::values_placeholders> streaming_values_placeholders{}; + constexpr streaming<stream_as::table_columns> streaming_table_column_names{}; + constexpr streaming<stream_as::non_generated_columns> streaming_non_generated_column_names{}; + constexpr streaming<stream_as::field_values_excluding> streaming_field_values_excluding{}; + constexpr streaming<stream_as::mapped_columns_expressions> streaming_mapped_columns_expressions{}; + constexpr streaming<stream_as::column_constraints> streaming_column_constraints{}; + + // serialize and stream a tuple of condition expressions; + // space + space-separated + template<class T, class Ctx> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::conditions_tuple>&, T, Ctx> tpl) { + const auto& conditions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(conditions, [&ss, &context](auto& c) { + ss << " " << serialize(c, context); + }); + return ss; + } + + // serialize and stream a tuple of action expressions; + // space-separated + template<class T, class Ctx> + std::ostream& operator<<(std::ostream& ss, std::tuple<const streaming<stream_as::actions_tuple>&, T, Ctx> tpl) { + const auto& actions = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(actions, [&ss, &context, first = true](auto& action) mutable { + constexpr std::array<const char*, 2> sep = {" ", ""}; + ss << sep[std::exchange(first, false)] << serialize(action, context); + }); + return ss; + } + + // serialize and stream a tuple of expressions; + // comma-separated + template<class T, class Ctx> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::expressions_tuple>&, T, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(arg, context); + }); + return ss; + } + + // serialize and stream multi_order_by arguments; + // comma-separated + template<class... Os, class Ctx> + std::ostream& operator<<( + std::ostream& ss, + std::tuple<const streaming<stream_as::expressions_tuple>&, const std::tuple<order_by_t<Os>...>&, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(args, [&ss, &context, first = true](auto& arg) mutable { + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize_order_by(arg, context); + }); + return ss; + } + + // serialize and stream a vector of expressions; + // comma-separated + template<class C, class Ctx> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::dynamic_expressions>&, C, Ctx> tpl) { + const auto& args = get<1>(tpl); + auto& context = get<2>(tpl); + + constexpr std::array<const char*, 2> sep = {", ", ""}; + for(size_t i = 0, first = true; i < args.size(); ++i) { + ss << sep[std::exchange(first, false)] << serialize(args[i], context); + } + return ss; + } + + // stream a vector of already serialized strings; + // comma-separated + template<class C> + std::ostream& operator<<(std::ostream& ss, std::tuple<const streaming<stream_as::serialized>&, C> tpl) { + const auto& strings = get<1>(tpl); + + constexpr std::array<const char*, 2> sep = {", ", ""}; + for(size_t i = 0, first = true; i < strings.size(); ++i) { + ss << sep[std::exchange(first, false)] << strings[i]; + } + return ss; + } + + // stream an identifier described by a variadic string pack, which is one of: + // 1. identifier + // 2. identifier, alias + // 3. qualifier, identifier, alias + template<class... Strings> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::identifier>&, Strings...> tpl) { + stream_identifier(ss, tpl, streaming_identifier.offset_index(std::index_sequence_for<Strings...>{})); + return ss; + } + + // stream a container of identifiers described by a string or a tuple, which is one of: + // 1. identifier + // 1. tuple(identifier) + // 2. tuple(identifier, alias), pair(identifier, alias) + // 3. tuple(qualifier, identifier, alias) + // + // comma-separated + template<class C> + std::ostream& operator<<(std::ostream& ss, std::tuple<const streaming<stream_as::identifiers>&, C> tpl) { + const auto& identifiers = get<1>(tpl); + + constexpr std::array<const char*, 2> sep = {", ", ""}; + bool first = true; + for(auto& identifier: identifiers) { + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, identifier); + } + return ss; + } + + // stream placeholders as part of a values clause + template<class... Ts> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::values_placeholders>&, Ts...> tpl) { + const size_t& columnsCount = get<1>(tpl); + const ptrdiff_t& valuesCount = get<2>(tpl); + + if(!valuesCount || !columnsCount) { + return ss; + } + + std::string result; + result.reserve((1 + (columnsCount * 1) + (columnsCount * 2 - 2) + 1) * valuesCount + (valuesCount * 2 - 2)); + + constexpr std::array<const char*, 2> sep = {", ", ""}; + for(ptrdiff_t i = 0, first = true; i < valuesCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "("; + for(size_t i = 0, first = true; i < columnsCount; ++i) { + result += sep[std::exchange(first, false)]; + result += "?"; + } + result += ")"; + } + ss << result; + return ss; + } + + // stream a table's column identifiers, possibly qualified; + // comma-separated + template<class Table> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::table_columns>&, Table, const bool&> tpl) { + const auto& table = get<1>(tpl); + const bool& qualified = get<2>(tpl); + + table.for_each_column([&ss, &tableName = qualified ? table.name : std::string{}, first = true]( + const column_identifier& column) mutable { + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, tableName, column.name, std::string{}); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template<class Table> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::non_generated_columns>&, Table> tpl) { + const auto& table = get<1>(tpl); + + table.template for_each_column_excluding<is_generated_always>( + [&ss, first = true](const column_identifier& column) mutable { + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, column.name); + }); + return ss; + } + + // stream a table's non-generated column identifiers, unqualified; + // comma-separated + template<class PredFnCls, class L, class Ctx, class Obj> + std::ostream& + operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::field_values_excluding>&, PredFnCls, L, Ctx, Obj> tpl) { + using check_if_excluded = polyfill::remove_cvref_t<std::tuple_element_t<1, decltype(tpl)>>; + auto& excluded = get<2>(tpl); + auto& context = get<3>(tpl); + auto& object = get<4>(tpl); + using object_type = polyfill::remove_cvref_t<decltype(object)>; + auto& table = pick_table<object_type>(context.db_objects); + + table.template for_each_column_excluding<check_if_excluded>(call_as_template_base<column_field>( + [&ss, &excluded, &context, &object, first = true](auto& column) mutable { + if(excluded(column)) { + return; + } + + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(column.member_pointer, object), context); + })); + return ss; + } + + // stream a tuple of mapped columns (which are member pointers or column pointers); + // comma-separated + template<class T, class Ctx> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::mapped_columns_expressions>&, T, Ctx> tpl) { + const auto& columns = get<1>(tpl); + auto& context = get<2>(tpl); + + iterate_tuple(columns, [&ss, &context, first = true](auto& colRef) mutable { + const std::string* columnName = find_column_name(context.db_objects, colRef); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; + } + + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)]; + stream_identifier(ss, *columnName); + }); + return ss; + } + + template<class... Op, class Ctx> + std::ostream& operator<<(std::ostream& ss, + std::tuple<const streaming<stream_as::column_constraints>&, + const column_constraints<Op...>&, + const bool&, + Ctx> tpl) { + const auto& column = get<1>(tpl); + const bool& isNotNull = get<2>(tpl); + auto& context = get<3>(tpl); + + using constraints_type = constraints_type_t<column_constraints<Op...>>; + constexpr size_t constraintsCount = std::tuple_size<constraints_type>::value; + if(constraintsCount) { + std::vector<std::string> constraintsStrings; + constraintsStrings.reserve(constraintsCount); + int primaryKeyIndex = -1; + int autoincrementIndex = -1; + int tupleIndex = 0; + iterate_tuple(column.constraints, + [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context]( + auto& constraint) { + using constraint_type = std::decay_t<decltype(constraint)>; + constraintsStrings.push_back(serialize(constraint, context)); + if(is_primary_key_v<constraint_type>) { + primaryKeyIndex = tupleIndex; + } else if(is_autoincrement_v<constraint_type>) { + autoincrementIndex = tupleIndex; + } + ++tupleIndex; + }); + if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { + iter_swap(constraintsStrings.begin() + primaryKeyIndex, + constraintsStrings.begin() + autoincrementIndex); + } + for(auto& str: constraintsStrings) { + ss << str << ' '; + } + } + if(isNotNull) { + ss << "NOT NULL "; + } + + return ss; + } + } +} + namespace sqlite_orm { namespace internal { struct storage_base; template<class T> - int getPragmaCallback(void* data, int argc, char** argv, char**) { - auto& res = *(T*)data; - if(argc) { - res = row_extractor<T>().extract(argv[0]); - } - return 0; + int getPragmaCallback(void* data, int argc, char** argv, char** x) { + return extract_single_value<T>(data, argc, argv, x); } template<> @@ -11391,6 +13113,20 @@ namespace sqlite_orm { this->_journal_mode = static_cast<decltype(this->_journal_mode)>(value); } + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + int application_id() { + return this->get_pragma<int>("application_id"); + } + + /** + * https://www.sqlite.org/pragma.html#pragma_application_id + */ + void application_id(int value) { + this->set_pragma("application_id", value); + } + int synchronous() { return this->get_pragma<int>("synchronous"); } @@ -11423,15 +13159,77 @@ namespace sqlite_orm { template<class T> std::vector<std::string> integrity_check(T table_name) { - std::ostringstream oss; - oss << "integrity_check(" << table_name << ")"; - return this->get_pragma<std::vector<std::string>>(oss.str()); + std::ostringstream ss; + ss << "integrity_check(" << table_name << ")" << std::flush; + return this->get_pragma<std::vector<std::string>>(ss.str()); } std::vector<std::string> integrity_check(int n) { - std::ostringstream oss; - oss << "integrity_check(" << n << ")"; - return this->get_pragma<std::vector<std::string>>(oss.str()); + std::ostringstream ss; + ss << "integrity_check(" << n << ")" << std::flush; + return this->get_pragma<std::vector<std::string>>(ss.str()); + } + + // will include generated columns in response as opposed to table_info + std::vector<sqlite_orm::table_xinfo> table_xinfo(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::vector<sqlite_orm::table_xinfo> result; + std::ostringstream ss; + ss << "PRAGMA " + "table_xinfo(" + << streaming_identifier(tableName) << ")" << std::flush; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector<sqlite_orm::table_xinfo>*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + auto hidden = std::atoi(argv[index++]); + res.emplace_back(cid, move(name), move(type), notnull, move(dflt_value), pk, hidden); + } + return 0; + }, + &result); + return result; + } + + std::vector<sqlite_orm::table_info> table_info(const std::string& tableName) const { + auto connection = this->get_connection(); + + std::ostringstream ss; + ss << "PRAGMA " + "table_info(" + << streaming_identifier(tableName) << ")" << std::flush; + std::vector<sqlite_orm::table_info> result; + perform_exec( + connection.get(), + ss.str(), + [](void* data, int argc, char** argv, char**) -> int { + auto& res = *(std::vector<sqlite_orm::table_info>*)data; + if(argc) { + auto index = 0; + auto cid = std::atoi(argv[index++]); + std::string name = argv[index++]; + std::string type = argv[index++]; + bool notnull = !!std::atoi(argv[index++]); + std::string dflt_value = argv[index] ? argv[index] : ""; + ++index; + auto pk = std::atoi(argv[index++]); + res.emplace_back(cid, move(name), move(type), notnull, move(dflt_value), pk); + } + return 0; + }, + &result); + return result; } private: @@ -11444,16 +13242,9 @@ namespace sqlite_orm { template<class T> T get_pragma(const std::string& name) { auto connection = this->get_connection(); - auto query = "PRAGMA " + name; T result; - auto db = connection.get(); - auto rc = sqlite3_exec(db, query.c_str(), getPragmaCallback<T>, &result, nullptr); - if(rc == SQLITE_OK) { - return result; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_exec(connection.get(), "PRAGMA " + name, getPragmaCallback<T>, &result); + return result; } /** @@ -11467,8 +13258,8 @@ namespace sqlite_orm { db = con.get(); } std::stringstream ss; - ss << "PRAGMA " << name << " = " << value; - internal::perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << value << std::flush; + perform_void_exec(db, ss.str()); } void set_pragma(const std::string& name, const sqlite_orm::journal_mode& value, sqlite3* db = nullptr) { @@ -11477,14 +13268,14 @@ namespace sqlite_orm { db = con.get(); } std::stringstream ss; - ss << "PRAGMA " << name << " = " << internal::to_string(value); - internal::perform_void_exec(db, ss.str()); + ss << "PRAGMA " << name << " = " << to_string(value) << std::flush; + perform_void_exec(db, ss.str()); } }; } } -// #include "limit_accesor.h" +// #include "limit_accessor.h" #include <sqlite3.h> #include <map> // std::map @@ -11497,10 +13288,10 @@ namespace sqlite_orm { namespace internal { - struct limit_accesor { + struct limit_accessor { using get_connection_t = std::function<connection_ref()>; - limit_accesor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} + limit_accessor(get_connection_t get_connection_) : get_connection(std::move(get_connection_)) {} int length() { return this->get(SQLITE_LIMIT_LENGTH); @@ -11627,6 +13418,7 @@ namespace sqlite_orm { // #include "transaction_guard.h" #include <functional> // std::function +#include <utility> // std::move // #include "connection_holder.h" @@ -11639,6 +13431,9 @@ namespace sqlite_orm { * Has explicit `commit()` and `rollback()` functions. After explicit function is fired * guard won't do anything in d-tor. Also you can set `commit_on_destroy` to true to * make it call `COMMIT` on destroy. + * + * Note: The guard's destructor is explicitly marked as potentially throwing, + * so exceptions that occur during commit or rollback are propagated to the caller. */ struct transaction_guard_t { /** @@ -11651,26 +13446,35 @@ namespace sqlite_orm { std::function<void()> commit_func_, std::function<void()> rollback_func_) : connection(std::move(connection_)), - commit_func(std::move(commit_func_)), rollback_func(std::move(rollback_func_)) {} + commit_func(move(commit_func_)), rollback_func(move(rollback_func_)) {} - ~transaction_guard_t() { + transaction_guard_t(transaction_guard_t&& other) : + commit_on_destroy(other.commit_on_destroy), connection(std::move(other.connection)), + commit_func(move(other.commit_func)), rollback_func(move(other.rollback_func)), + gotta_fire(other.gotta_fire) { + other.gotta_fire = false; + } + + ~transaction_guard_t() noexcept(false) { if(this->gotta_fire) { - if(!this->commit_on_destroy) { - this->rollback_func(); - } else { + if(this->commit_on_destroy) { this->commit_func(); + } else { + this->rollback_func(); } } } + transaction_guard_t& operator=(transaction_guard_t&&) = delete; + /** * Call `COMMIT` explicitly. After this call * guard will not call `COMMIT` or `ROLLBACK` * in its destructor. */ void commit() { - this->commit_func(); this->gotta_fire = false; + this->commit_func(); } /** @@ -11679,8 +13483,8 @@ namespace sqlite_orm { * in its destructor. */ void rollback() { - this->rollback_func(); this->gotta_fire = false; + this->rollback_func(); } protected: @@ -11692,23 +13496,17 @@ namespace sqlite_orm { } } -// #include "statement_finalizer.h" - -// #include "type_printer.h" - -// #include "tuple_helper/tuple_helper.h" - // #include "row_extractor.h" -// #include "util.h" - // #include "connection_holder.h" // #include "backup.h" #include <sqlite3.h> +#include <system_error> // std::system_error #include <string> // std::string #include <memory> +#include <utility> // std::move, std::exchange // #include "error_code.h" @@ -11731,21 +13529,19 @@ namespace sqlite_orm { const std::string& zSourceName, std::unique_ptr<connection_holder> holder_) : handle(sqlite3_backup_init(to_.get(), zDestName.c_str(), from_.get(), zSourceName.c_str())), - to(to_), from(from_), holder(move(holder_)) { + holder(move(holder_)), to(to_), from(from_) { if(!this->handle) { - throw std::system_error(std::make_error_code(orm_error_code::failed_to_init_a_backup)); + throw std::system_error{orm_error_code::failed_to_init_a_backup}; } } backup_t(backup_t&& other) : - handle(other.handle), to(other.to), from(other.from), holder(move(other.holder)) { - other.handle = nullptr; - } + handle(std::exchange(other.handle, nullptr)), holder(move(other.holder)), to(other.to), + from(other.from) {} ~backup_t() { if(this->handle) { (void)sqlite3_backup_finish(this->handle); - this->handle = nullptr; } } @@ -11772,9 +13568,9 @@ namespace sqlite_orm { protected: sqlite3_backup* handle = nullptr; + std::unique_ptr<connection_holder> holder; connection_ref to; connection_ref from; - std::unique_ptr<connection_holder> holder; }; } } @@ -11784,7 +13580,10 @@ namespace sqlite_orm { // #include "values_to_tuple.h" #include <sqlite3.h> -#include <tuple> // std::get, std::tuple_element +#include <type_traits> // std::index_sequence, std::make_index_sequence +#include <tuple> // std::tuple, std::tuple_size, std::get + +// #include "functional/cxx_universal.h" // #include "row_extractor.h" @@ -11873,7 +13672,7 @@ namespace sqlite_orm { if(this->index < int(this->container.size()) && this->index >= 0) { return this->currentValue; } else { - throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + throw std::system_error{orm_error_code::index_is_out_of_bounds}; } } @@ -11909,10 +13708,10 @@ namespace sqlite_orm { arg_value operator[](int index) const { if(index < this->argsCount && index >= 0) { - auto valuePointer = this->values[index]; - return {valuePointer}; + sqlite3_value* value = this->values[index]; + return {value}; } else { - throw std::system_error(std::make_error_code(orm_error_code::index_is_out_of_bounds)); + throw std::system_error{orm_error_code::index_is_out_of_bounds}; } } @@ -11938,32 +13737,34 @@ namespace sqlite_orm { namespace internal { - /** - * T is a std::tuple type - * I is index to extract value from values C array to tuple. I must me < std::tuple_size<T>::value - */ - template<class T, int I> struct values_to_tuple { - - void extract(sqlite3_value** values, T& tuple, int argsCount) const { - using element_type = typename std::tuple_element<I, T>::type; - std::get<I>(tuple) = row_extractor<element_type>().extract(values[I]); - - values_to_tuple<T, I - 1>().extract(values, tuple, argsCount); + template<class Tpl> + void operator()(sqlite3_value** values, Tpl& tuple, int /*argsCount*/) const { + (*this)(values, tuple, std::make_index_sequence<std::tuple_size<Tpl>::value>{}); } - }; - template<class T> - struct values_to_tuple<T, -1> { - void extract(sqlite3_value** values, T& tuple, int argsCount) const { - //.. + void operator()(sqlite3_value** values, std::tuple<arg_values>& tuple, int argsCount) const { + std::get<0>(tuple) = arg_values(argsCount, values); } - }; - template<> - struct values_to_tuple<std::tuple<arg_values>, 0> { - void extract(sqlite3_value** values, std::tuple<arg_values>& tuple, int argsCount) const { - std::get<0>(tuple) = arg_values(argsCount, values); + private: +#ifdef SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED + template<class Tpl, size_t... Idx> + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence<Idx...>) const { + (this->extract(values[Idx], std::get<Idx>(tuple)), ...); + } +#else + template<class Tpl, size_t I, size_t... Idx> + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence<I, Idx...>) const { + this->extract(values[I], std::get<I>(tuple)); + (*this)(values, tuple, std::index_sequence<Idx...>{}); + } + template<class Tpl, size_t... Idx> + void operator()(sqlite3_value** /*values*/, Tpl&, std::index_sequence<Idx...>) const {} +#endif + template<class T> + void extract(sqlite3_value* value, T& t) const { + t = row_extractor<T>{}.extract(value); } }; } @@ -11971,6 +13772,10 @@ namespace sqlite_orm { // #include "arg_values.h" +// #include "util.h" + +// #include "serializing_util.h" + namespace sqlite_orm { namespace internal { @@ -11980,42 +13785,93 @@ namespace sqlite_orm { std::function<void(sqlite3*)> on_open; pragma_t pragma; - limit_accesor limit; + limit_accessor limit; transaction_guard_t transaction_guard() { this->begin_transaction(); - auto commitFunc = std::bind(static_cast<void (storage_base::*)()>(&storage_base::commit), this); - auto rollbackFunc = std::bind(static_cast<void (storage_base::*)()>(&storage_base::rollback), this); - return {this->get_connection(), move(commitFunc), move(rollbackFunc)}; + return {this->get_connection(), + std::bind(&storage_base::commit, this), + std::bind(&storage_base::rollback, this)}; } void drop_index(const std::string& indexName) { std::stringstream ss; - ss << "DROP INDEX '" << indexName + "'"; - perform_void_exec(get_connection().get(), ss.str()); + ss << "DROP INDEX " << quote_identifier(indexName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); + } + + void drop_trigger(const std::string& triggerName) { + std::stringstream ss; + ss << "DROP TRIGGER " << quote_identifier(triggerName) << std::flush; + perform_void_exec(this->get_connection().get(), ss.str()); } void vacuum() { - perform_void_exec(get_connection().get(), "VACUUM"); + perform_void_exec(this->get_connection().get(), "VACUUM"); } /** * Drops table with given name. */ void drop_table(const std::string& tableName) { - auto con = this->get_connection(); - this->drop_table_internal(tableName, con.get()); + this->drop_table_internal(this->get_connection().get(), tableName); } /** * Rename table named `from` to `to`. */ void rename_table(const std::string& from, const std::string& to) { + this->rename_table(this->get_connection().get(), from, to); + } + + protected: + void rename_table(sqlite3* db, const std::string& oldName, const std::string& newName) const { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(oldName) << " RENAME TO " << streaming_identifier(newName) + << std::flush; + perform_void_exec(db, ss.str()); + } + + /** + * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. + * Note: table can be not mapped to a storage + * @return true if table with a given name exists in db, false otherwise. + */ + bool table_exists(const std::string& tableName) { + auto con = this->get_connection(); + return this->table_exists(con.get(), tableName); + } + + bool table_exists(sqlite3* db, const std::string& tableName) const { + bool result = false; std::stringstream ss; - ss << "ALTER TABLE '" << from << "' RENAME TO '" << to << "'"; - perform_void_exec(get_connection().get(), ss.str()); + ss << "SELECT COUNT(*) FROM sqlite_master WHERE type = " << streaming_identifier("table") + << " AND name = " << quote_string_literal(tableName) << std::flush; + perform_exec( + db, + ss.str(), + [](void* data, int argc, char** argv, char** /*azColName*/) -> int { + auto& res = *(bool*)data; + if(argc) { + res = !!std::atoi(argv[0]); + } + return 0; + }, + &result); + return result; } + void add_generated_cols(std::vector<const table_xinfo*>& columnsToAdd, + const std::vector<table_xinfo>& storageTableInfo) { + // iterate through storage columns + for(const table_xinfo& storageColumnInfo: storageTableInfo) { + if(storageColumnInfo.hidden) { + columnsToAdd.push_back(&storageColumnInfo); + } + } + } + + public: /** * sqlite3_changes function. */ @@ -12043,21 +13899,15 @@ namespace sqlite_orm { } /** - * Returns libsqltie3 lib version, not sqlite_orm + * Returns libsqlite3 version, not sqlite_orm */ std::string libversion() { return sqlite3_libversion(); } bool transaction(const std::function<bool()>& f) { - auto guard = transaction_guard(); - auto shouldCommit = f(); - if(shouldCommit) { - guard.commit(); - } else { - guard.rollback(); - } - return shouldCommit; + auto guard = this->transaction_guard(); + return guard.commit_on_destroy = f(); } std::string current_timestamp() { @@ -12086,28 +13936,20 @@ namespace sqlite_orm { std::vector<std::string> table_names() { auto con = this->get_connection(); std::vector<std::string> tableNames; - std::string sql = "SELECT name FROM sqlite_master WHERE type='table'"; using data_t = std::vector<std::string>; - auto db = con.get(); - int res = sqlite3_exec( - db, - sql.c_str(), + perform_exec( + con.get(), + "SELECT name FROM sqlite_master WHERE type='table'", [](void* data, int argc, char** argv, char** /*columnName*/) -> int { auto& tableNames_ = *(data_t*)data; - for(int i = 0; i < argc; i++) { + for(int i = 0; i < argc; ++i) { if(argv[i]) { - tableNames_.push_back(argv[i]); + tableNames_.emplace_back(argv[i]); } } return 0; }, - &tableNames, - nullptr); - - if(res != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + &tableNames); return tableNames; } @@ -12134,21 +13976,22 @@ namespace sqlite_orm { * } * }; * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. */ template<class F> void create_scalar_function() { - static_assert(is_scalar_function<F>::value, "F cannot be a scalar function"); + static_assert(is_scalar_function_v<F>, "F can't be an aggregate function"); std::stringstream ss; - ss << F::name(); + ss << F::name() << std::flush; auto name = ss.str(); using args_tuple = typename callable_arguments<F>::args_tuple; using return_type = typename callable_arguments<F>::return_type; - auto argsCount = int(std::tuple_size<args_tuple>::value); - if(std::is_same<args_tuple, std::tuple<arg_values>>::value) { - argsCount = -1; - } - this->scalarFunctions.emplace_back(new scalar_function_t{ + constexpr auto argsCount = std::is_same<args_tuple, std::tuple<arg_values>>::value + ? -1 + : int(std::tuple_size<args_tuple>::value); + this->scalarFunctions.emplace_back(new user_defined_scalar_function_t{ move(name), argsCount, []() -> int* { @@ -12156,19 +13999,19 @@ namespace sqlite_orm { }, /* call = */ [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& functionPointer = *static_cast<F*>(functionVoidPointer); + auto& function = *static_cast<F*>(functionVoidPointer); args_tuple argsTuple; - using tuple_size = std::tuple_size<args_tuple>; - values_to_tuple<args_tuple, tuple_size::value - 1>().extract(values, argsTuple, argsCount); - auto result = call(functionPointer, std::move(argsTuple)); + values_to_tuple{}(values, argsTuple, argsCount); + auto result = call(function, std::move(argsTuple)); statement_binder<return_type>().result(context, result); }, delete_function_callback<F>, }); if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); - try_to_create_function(db, static_cast<scalar_function_t&>(*this->scalarFunctions.back())); + sqlite3* db = this->connection->get(); + try_to_create_function(db, + static_cast<user_defined_scalar_function_t&>(*this->scalarFunctions.back())); } } @@ -12194,21 +14037,22 @@ namespace sqlite_orm { * } * }; * ``` + * + * Note: Currently, a function's name must not contain white-space characters, because it doesn't get quoted. */ template<class F> void create_aggregate_function() { - static_assert(is_aggregate_function<F>::value, "F cannot be an aggregate function"); + static_assert(is_aggregate_function_v<F>, "F can't be a scalar function"); std::stringstream ss; - ss << F::name(); + ss << F::name() << std::flush; auto name = ss.str(); using args_tuple = typename callable_arguments<F>::args_tuple; using return_type = typename callable_arguments<F>::return_type; - auto argsCount = int(std::tuple_size<args_tuple>::value); - if(std::is_same<args_tuple, std::tuple<arg_values>>::value) { - argsCount = -1; - } - this->aggregateFunctions.emplace_back(new aggregate_function_t{ + constexpr auto argsCount = std::is_same<args_tuple, std::tuple<arg_values>>::value + ? -1 + : int(std::tuple_size<args_tuple>::value); + this->aggregateFunctions.emplace_back(new user_defined_aggregate_function_t{ move(name), argsCount, /* create = */ @@ -12216,25 +14060,26 @@ namespace sqlite_orm { return (int*)(new F()); }, /* step = */ - [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& functionPointer = *static_cast<F*>(functionVoidPointer); + [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { + auto& function = *static_cast<F*>(functionVoidPointer); args_tuple argsTuple; - using tuple_size = std::tuple_size<args_tuple>; - values_to_tuple<args_tuple, tuple_size::value - 1>().extract(values, argsTuple, argsCount); - call(functionPointer, &F::step, move(argsTuple)); + values_to_tuple{}(values, argsTuple, argsCount); + call(function, &F::step, move(argsTuple)); }, /* finalCall = */ [](sqlite3_context* context, void* functionVoidPointer) { - auto& functionPointer = *static_cast<F*>(functionVoidPointer); - auto result = functionPointer.fin(); + auto& function = *static_cast<F*>(functionVoidPointer); + auto result = function.fin(); statement_binder<return_type>().result(context, result); }, delete_function_callback<F>, }); if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); - try_to_create_function(db, static_cast<aggregate_function_t&>(*this->aggregateFunctions.back())); + sqlite3* db = this->connection->get(); + try_to_create_function( + db, + static_cast<user_defined_aggregate_function_t&>(*this->aggregateFunctions.back())); } } @@ -12243,11 +14088,10 @@ namespace sqlite_orm { */ template<class F> void delete_scalar_function() { - static_assert(is_scalar_function<F>::value, "F cannot be a scalar function"); + static_assert(is_scalar_function_v<F>, "F cannot be an aggregate function"); std::stringstream ss; - ss << F::name(); - auto name = ss.str(); - this->delete_function_impl(name, this->scalarFunctions); + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), this->scalarFunctions); } /** @@ -12255,11 +14099,10 @@ namespace sqlite_orm { */ template<class F> void delete_aggregate_function() { - static_assert(is_aggregate_function<F>::value, "F cannot be an aggregate function"); + static_assert(is_aggregate_function_v<F>, "F cannot be a scalar function"); std::stringstream ss; - ss << F::name(); - auto name = ss.str(); - this->delete_function_impl(name, this->aggregateFunctions); + ss << F::name() << std::flush; + this->delete_function_impl(ss.str(), this->aggregateFunctions); } template<class C> @@ -12269,32 +14112,29 @@ namespace sqlite_orm { return collatingObject(leftLength, lhs, rightLength, rhs); }; std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - this->create_collation(name, move(func)); + ss << C::name() << std::flush; + this->create_collation(ss.str(), move(func)); } void create_collation(const std::string& name, collating_function f) { - collating_function* functionPointer = nullptr; + collating_function* function = nullptr; const auto functionExists = bool(f); if(functionExists) { - functionPointer = &(collatingFunctions[name] = std::move(f)); + function = &(collatingFunctions[name] = std::move(f)); } else { collatingFunctions.erase(name); } // create collations if db is open if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); auto resultCode = sqlite3_create_collation(db, name.c_str(), SQLITE_UTF8, - functionPointer, + function, functionExists ? collate_callback : nullptr); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } } @@ -12302,36 +14142,41 @@ namespace sqlite_orm { template<class C> void delete_collation() { std::stringstream ss; - ss << C::name(); - auto name = ss.str(); - ss.flush(); - this->create_collation(name, {}); + ss << C::name() << std::flush; + this->create_collation(ss.str(), {}); } void begin_transaction() { - this->connection->retain(); - if(1 == this->connection->retain_count()) { - this->on_open_internal(this->connection->get()); - } - auto db = this->connection->get(); - perform_void_exec(db, "BEGIN TRANSACTION"); + this->begin_transaction_internal("BEGIN TRANSACTION"); + } + + void begin_deferred_transaction() { + this->begin_transaction_internal("BEGIN DEFERRED TRANSACTION"); + } + + void begin_immediate_transaction() { + this->begin_transaction_internal("BEGIN IMMEDIATE TRANSACTION"); + } + + void begin_exclusive_transaction() { + this->begin_transaction_internal("BEGIN EXCLUSIVE TRANSACTION"); } void commit() { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); perform_void_exec(db, "COMMIT"); this->connection->release(); if(this->connection->retain_count() < 0) { - throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); + throw std::system_error{orm_error_code::no_active_transaction}; } } void rollback() { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); perform_void_exec(db, "ROLLBACK"); this->connection->release(); if(this->connection->retain_count() < 0) { - throw std::system_error(std::make_error_code(orm_error_code::no_active_transaction)); + throw std::system_error{orm_error_code::no_active_transaction}; } } @@ -12387,6 +14232,15 @@ namespace sqlite_orm { return this->connection->retain_count() > 0; } + /* + * returning false when there is a transaction in place + * otherwise true; function is not const because it has to call get_connection() + */ + bool get_autocommit() { + auto con = this->get_connection(); + return sqlite3_get_autocommit(con.get()); + } + int busy_handler(std::function<int(int)> handler) { _busy_handler = move(handler); if(this->is_opened()) { @@ -12401,11 +14255,12 @@ namespace sqlite_orm { } protected: - storage_base(const std::string& filename_, int foreignKeysCount) : + storage_base(std::string filename, int foreignKeysCount) : pragma(std::bind(&storage_base::get_connection, this)), limit(std::bind(&storage_base::get_connection, this)), - inMemory(filename_.empty() || filename_ == ":memory:"), - connection(std::make_unique<connection_holder>(filename_)), cachedForeignKeysCount(foreignKeysCount) { + inMemory(filename.empty() || filename == ":memory:"), + connection(std::make_unique<connection_holder>(move(filename))), + cachedForeignKeysCount(foreignKeysCount) { if(this->inMemory) { this->connection->retain(); this->on_open_internal(this->connection->get()); @@ -12432,7 +14287,16 @@ namespace sqlite_orm { } } - public: + void begin_transaction_internal(const std::string& query) { + this->connection->retain(); + if(1 == this->connection->retain_count()) { + this->on_open_internal(this->connection->get()); + } + sqlite3* db = this->connection->get(); + perform_void_exec(db, query); + } + + public: connection_ref get_connection() { connection_ref res{*this->connection}; if(1 == this->connection->retain_count()) { @@ -12445,29 +14309,13 @@ namespace sqlite_orm { void foreign_keys(sqlite3* db, bool value) { std::stringstream ss; - ss << "PRAGMA foreign_keys = " << value; + ss << "PRAGMA foreign_keys = " << value << std::flush; perform_void_exec(db, ss.str()); } bool foreign_keys(sqlite3* db) { - std::string query = "PRAGMA foreign_keys"; - auto result = false; - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char**) -> int { - auto& res = *(bool*)data; - if(argc) { - res = row_extractor<bool>().extract(argv[0]); - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + bool result = false; + perform_exec(db, "PRAGMA foreign_keys", extract_single_value<bool>, &result); return result; } @@ -12491,8 +14339,7 @@ namespace sqlite_orm { auto resultCode = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } @@ -12505,11 +14352,11 @@ namespace sqlite_orm { } for(auto& functionPointer: this->scalarFunctions) { - try_to_create_function(db, static_cast<scalar_function_t&>(*functionPointer)); + try_to_create_function(db, static_cast<user_defined_scalar_function_t&>(*functionPointer)); } for(auto& functionPointer: this->aggregateFunctions) { - try_to_create_function(db, static_cast<aggregate_function_t&>(*functionPointer)); + try_to_create_function(db, static_cast<user_defined_aggregate_function_t&>(*functionPointer)); } if(this->on_open) { @@ -12518,7 +14365,7 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, - std::vector<std::unique_ptr<function_base>>& functionsVector) const { + std::vector<std::unique_ptr<user_defined_function_base>>& functionsVector) const { auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { return functionPointer->name == name; }); @@ -12527,7 +14374,7 @@ namespace sqlite_orm { it = functionsVector.end(); if(this->connection->retain_count() > 0) { - auto db = this->connection->get(); + sqlite3* db = this->connection->get(); auto resultCode = sqlite3_create_function_v2(db, name.c_str(), 0, @@ -12538,16 +14385,15 @@ namespace sqlite_orm { nullptr, nullptr); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } } else { - throw std::system_error(std::make_error_code(orm_error_code::function_not_found)); + throw std::system_error{orm_error_code::function_not_found}; } } - void try_to_create_function(sqlite3* db, scalar_function_t& function) { + void try_to_create_function(sqlite3* db, user_defined_scalar_function_t& function) { auto resultCode = sqlite3_create_function_v2(db, function.name.c_str(), function.argumentsCount, @@ -12558,12 +14404,11 @@ namespace sqlite_orm { nullptr, nullptr); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); + throw_translated_sqlite_error(db); } } - void try_to_create_function(sqlite3* db, aggregate_function_t& function) { + void try_to_create_function(sqlite3* db, user_defined_aggregate_function_t& function) { auto resultCode = sqlite3_create_function(db, function.name.c_str(), function.argumentsCount, @@ -12573,15 +14418,14 @@ namespace sqlite_orm { aggregate_function_step_callback, aggregate_function_final_callback); if(resultCode != SQLITE_OK) { - throw std::system_error(std::error_code(resultCode, get_sqlite_error_category()), - sqlite3_errstr(resultCode)); + throw_translated_sqlite_error(resultCode); } } static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast<aggregate_function_t*>(functionVoidPointer); + auto functionPointer = static_cast<user_defined_aggregate_function_t*>(functionVoidPointer); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast<int**>(aggregateContextVoidPointer); if(*aggregateContextIntPointer == nullptr) { @@ -12592,7 +14436,7 @@ namespace sqlite_orm { static void aggregate_function_final_callback(sqlite3_context* context) { auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast<aggregate_function_t*>(functionVoidPointer); + auto functionPointer = static_cast<user_defined_aggregate_function_t*>(functionVoidPointer); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast<int**>(aggregateContextVoidPointer); functionPointer->finalCall(context, *aggregateContextIntPointer); @@ -12601,11 +14445,11 @@ namespace sqlite_orm { static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast<scalar_function_t*>(functionVoidPointer); + auto functionPointer = static_cast<user_defined_scalar_function_t*>(functionVoidPointer); std::unique_ptr<int, void (*)(int*)> callablePointer(functionPointer->create(), functionPointer->destroy); if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { - throw std::system_error(std::make_error_code(orm_error_code::arguments_count_does_not_match)); + throw std::system_error{orm_error_code::arguments_count_does_not_match}; } functionPointer->run(context, functionPointer, argsCount, values); } @@ -12619,33 +14463,13 @@ namespace sqlite_orm { std::string current_timestamp(sqlite3* db) { std::string result; - std::stringstream ss; - ss << "SELECT CURRENT_TIMESTAMP"; - auto query = ss.str(); - auto rc = sqlite3_exec( - db, - query.c_str(), - [](void* data, int argc, char** argv, char**) -> int { - auto& res = *(std::string*)data; - if(argc) { - if(argv[0]) { - res = row_extractor<std::string>().extract(argv[0]); - } - } - return 0; - }, - &result, - nullptr); - if(rc != SQLITE_OK) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + perform_exec(db, "SELECT CURRENT_TIMESTAMP", extract_single_value<std::string>, &result); return result; } - void drop_table_internal(const std::string& tableName, sqlite3* db) { + void drop_table_internal(sqlite3* db, const std::string& tableName) { std::stringstream ss; - ss << "DROP TABLE '" << tableName + "'"; + ss << "DROP TABLE " << streaming_identifier(tableName) << std::flush; perform_void_exec(db, ss.str()); } @@ -12663,14 +14487,44 @@ namespace sqlite_orm { } } - // returns foreign keys count in storage definition - template<class T> - static int foreign_keys_count(T& storageImpl) { - auto res = 0; - storageImpl.for_each([&res](auto& impl) { - res += impl.foreign_keys_count(); - }); - return res; + bool calculate_remove_add_columns(std::vector<const table_xinfo*>& columnsToAdd, + std::vector<table_xinfo>& storageTableInfo, + std::vector<table_xinfo>& dbTableInfo) const { + bool notEqual = false; + + // iterate through storage columns + for(size_t storageColumnInfoIndex = 0; storageColumnInfoIndex < storageTableInfo.size(); + ++storageColumnInfoIndex) { + + // get storage's column info + auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + auto& columnName = storageColumnInfo.name; + + // search for a column in db eith the same name + auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { + return ti.name == columnName; + }); + if(dbColumnInfoIt != dbTableInfo.end()) { + auto& dbColumnInfo = *dbColumnInfoIt; + auto columnsAreEqual = + dbColumnInfo.name == storageColumnInfo.name && + dbColumnInfo.notnull == storageColumnInfo.notnull && + (!dbColumnInfo.dflt_value.empty()) == (!storageColumnInfo.dflt_value.empty()) && + dbColumnInfo.pk == storageColumnInfo.pk && + (dbColumnInfo.hidden == 0) == (storageColumnInfo.hidden == 0); + if(!columnsAreEqual) { + notEqual = true; + break; + } + dbTableInfo.erase(dbColumnInfoIt); + storageTableInfo.erase(storageTableInfo.begin() + + static_cast<ptrdiff_t>(storageColumnInfoIndex)); + --storageColumnInfoIndex; + } else { + columnsToAdd.push_back(&storageColumnInfo); + } + } + return notEqual; } const bool inMemory; @@ -12679,8 +14533,8 @@ namespace sqlite_orm { std::map<std::string, collating_function> collatingFunctions; const int cachedForeignKeysCount; std::function<int(int)> _busy_handler; - std::vector<std::unique_ptr<function_base>> scalarFunctions; - std::vector<std::unique_ptr<function_base>> aggregateFunctions; + std::vector<std::unique_ptr<user_defined_function_base>> scalarFunctions; + std::vector<std::unique_ptr<user_defined_function_base>> aggregateFunctions; }; } } @@ -12702,24 +14556,16 @@ namespace sqlite_orm { struct expression_object_type; template<class T> - struct expression_object_type<update_t<T>> { - using type = typename std::decay<T>::type; - }; + struct expression_object_type<update_t<T>> : std::decay<T> {}; template<class T> - struct expression_object_type<update_t<std::reference_wrapper<T>>> { - using type = typename std::decay<T>::type; - }; + struct expression_object_type<update_t<std::reference_wrapper<T>>> : std::decay<T> {}; template<class T> - struct expression_object_type<replace_t<T>> { - using type = typename std::decay<T>::type; - }; + struct expression_object_type<replace_t<T>> : std::decay<T> {}; template<class T> - struct expression_object_type<replace_t<std::reference_wrapper<T>>> { - using type = typename std::decay<T>::type; - }; + struct expression_object_type<replace_t<std::reference_wrapper<T>>> : std::decay<T> {}; template<class It, class L, class O> struct expression_object_type<replace_range_t<It, L, O>> { @@ -12731,19 +14577,24 @@ namespace sqlite_orm { using type = typename replace_range_t<std::reference_wrapper<It>, L, O>::object_type; }; - template<class T> - struct expression_object_type<insert_t<T>> { - using type = typename std::decay<T>::type; + template<class T, class... Ids> + struct expression_object_type<remove_t<T, Ids...>> { + using type = T; }; - template<class T> - struct expression_object_type<insert_t<std::reference_wrapper<T>>> { - using type = typename std::decay<T>::type; + template<class T, class... Ids> + struct expression_object_type<remove_t<std::reference_wrapper<T>, Ids...>> { + using type = T; }; + template<class T> + struct expression_object_type<insert_t<T>> : std::decay<T> {}; + + template<class T> + struct expression_object_type<insert_t<std::reference_wrapper<T>>> : std::decay<T> {}; + template<class It, class L, class O> struct expression_object_type<insert_range_t<It, L, O>> { - using transformer_type = L; using type = typename insert_range_t<It, L, O>::object_type; }; @@ -12753,14 +14604,10 @@ namespace sqlite_orm { }; template<class T, class... Cols> - struct expression_object_type<insert_explicit<T, Cols...>> { - using type = typename std::decay<T>::type; - }; + struct expression_object_type<insert_explicit<T, Cols...>> : std::decay<T> {}; template<class T, class... Cols> - struct expression_object_type<insert_explicit<std::reference_wrapper<T>, Cols...>> { - using type = typename std::decay<T>::type; - }; + struct expression_object_type<insert_explicit<std::reference_wrapper<T>, Cols...>> : std::decay<T> {}; template<class T> struct get_ref_t { @@ -12782,7 +14629,7 @@ namespace sqlite_orm { template<class T> auto& get_ref(T& t) { - using arg_type = typename std::decay<T>::type; + using arg_type = std::decay_t<T>; get_ref_t<arg_type> g; return g(t); } @@ -12795,7 +14642,7 @@ namespace sqlite_orm { template<class T> auto& get_object(T& t) { - using expression_type = typename std::decay<T>::type; + using expression_type = std::decay_t<T>; get_object_t<expression_type> obj; return obj(t); } @@ -12806,7 +14653,7 @@ namespace sqlite_orm { template<class O> auto& operator()(O& e) const { - return get_ref(e.obj); + return get_ref(e.object); } }; @@ -12816,7 +14663,7 @@ namespace sqlite_orm { template<class O> auto& operator()(O& e) const { - return get_ref(e.obj); + return get_ref(e.object); } }; @@ -12826,22 +14673,41 @@ namespace sqlite_orm { template<class O> auto& operator()(O& e) const { - return get_ref(e.obj); + return get_ref(e.object); } }; } } -// #include "statement_serializator.h" +// #include "statement_serializer.h" #include <sstream> // std::stringstream #include <string> // std::string #include <type_traits> // std::enable_if, std::remove_pointer #include <vector> // std::vector #include <algorithm> // std::iter_swap -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +#ifndef SQLITE_ORM_OMITS_CODECVT +#include <codecvt> // std::codecvt_utf8_utf16 +#endif // SQLITE_ORM_OMITS_CODECVT +#include <memory> +#include <array> +// #include "functional/cxx_string_view.h" + +// #include "functional/cxx_universal.h" + +// #include "functional/cxx_functional_polyfill.h" + +// #include "functional/mpl.h" + +// #include "tuple_helper/tuple_filter.h" + +// #include "ast/upsert_clause.h" + +// #include "ast/excluded.h" + +// #include "ast/group_by.h" + +// #include "ast/into.h" // #include "core_functions.h" @@ -12851,10 +14717,22 @@ namespace sqlite_orm { // #include "column.h" +// #include "indexed_column.h" + +// #include "function.h" + +// #include "prepared_statement.h" + // #include "rowid.h" +// #include "pointer_value.h" + // #include "type_printer.h" +// #include "field_printer.h" + +// #include "literal.h" + // #include "table_name_collector.h" #include <set> // std::set @@ -12862,6 +14740,10 @@ namespace sqlite_orm { #include <functional> // std::function #include <typeindex> // std::type_index +// #include "functional/cxx_type_traits_polyfill.h" + +// #include "type_traits.h" + // #include "select_constraints.h" // #include "alias.h" @@ -12874,14 +14756,14 @@ namespace sqlite_orm { struct table_name_collector { using table_name_set = std::set<std::pair<std::string, std::string>>; - using find_table_name_t = std::function<std::string(std::type_index)>; + using find_table_name_t = std::function<std::string(const std::type_index&)>; find_table_name_t find_table_name; mutable table_name_set table_names; table_name_collector() = default; - table_name_collector(find_table_name_t _find_table_name) : find_table_name(std::move(_find_table_name)) {} + table_name_collector(find_table_name_t find_table_name) : find_table_name{move(find_table_name)} {} template<class T> table_name_set operator()(const T&) const { @@ -12890,16 +14772,12 @@ namespace sqlite_orm { template<class F, class O> void operator()(F O::*, std::string alias = {}) const { - if(this->find_table_name) { - table_names.insert(std::make_pair(this->find_table_name(typeid(O)), move(alias))); - } + table_names.emplace(this->find_table_name(typeid(O)), move(alias)); } template<class T, class F> void operator()(const column_pointer<T, F>&) const { - if(this->find_table_name) { - table_names.insert({this->find_table_name(typeid(T)), ""}); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template<class T, class C> @@ -12909,52 +14787,44 @@ namespace sqlite_orm { template<class T> void operator()(const count_asterisk_t<T>&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - if(!tableName.empty()) { - table_names.insert(std::make_pair(move(tableName), "")); - } + auto tableName = this->find_table_name(typeid(T)); + if(!tableName.empty()) { + table_names.emplace(move(tableName), ""); } } - template<class T> + template<class T, satisfies_not<std::is_base_of, alias_tag, T> = true> void operator()(const asterisk_t<T>&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); + } + + template<class T, satisfies<std::is_base_of, alias_tag, T> = true> + void operator()(const asterisk_t<T>&) const { + // note: not all alias classes have a nested A::type + static_assert(polyfill::is_detected_v<type_t, T>, + "alias<O> must have a nested alias<O>::type typename"); + auto tableName = this->find_table_name(typeid(type_t<T>)); + table_names.emplace(move(tableName), alias_extractor<T>::get()); } template<class T> void operator()(const object_t<T>&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template<class T> void operator()(const table_rowid_t<T>&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template<class T> void operator()(const table_oid_t<T>&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } template<class T> void operator()(const table__rowid_t<T>&) const { - if(this->find_table_name) { - auto tableName = this->find_table_name(typeid(T)); - table_names.insert(std::make_pair(move(tableName), "")); - } + table_names.emplace(this->find_table_name(typeid(T)), ""); } }; @@ -12964,63 +14834,90 @@ namespace sqlite_orm { // #include "column_names_getter.h" +#include <system_error> // std::system_error #include <string> // std::string #include <vector> // std::vector #include <functional> // std::reference_wrapper // #include "error_code.h" +// #include "serializer_context.h" + // #include "select_constraints.h" +// #include "serializing_util.h" + +// #include "util.h" + namespace sqlite_orm { namespace internal { - template<class T, class C> - std::string serialize(const T& t, const C& context); + template<class T, class I> + std::string serialize(const T& t, const serializer_context<I>& context); template<class T, class SFINAE = void> struct column_names_getter { using expression_type = T; - template<class C> - std::vector<std::string> operator()(const expression_type& t, const C& context) { + template<class Ctx> + std::vector<std::string> operator()(const expression_type& t, const Ctx& context) const { auto newContext = context; newContext.skip_table_name = false; auto columnName = serialize(t, newContext); - if(columnName.length()) { + if(!columnName.empty()) { return {move(columnName)}; } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } } }; - template<class T, class C> - std::vector<std::string> get_column_names(const T& t, const C& context) { - column_names_getter<T> serializator; - return serializator(t, context); + template<class T, class Ctx> + std::vector<std::string> get_column_names(const T& t, const Ctx& context) { + column_names_getter<T> serializer; + return serializer(t, context); + } + + template<class T, class Ctx> + std::vector<std::string> collect_table_column_names(bool definedOrder, const Ctx& context) { + if(definedOrder) { + std::vector<std::string> quotedNames; + auto& table = pick_table<mapped_type_proxy_t<T>>(context.db_objects); + quotedNames.reserve(table.count_columns_amount()); + table.for_each_column(["edNames](const column_identifier& column) { + if(std::is_base_of<alias_tag, T>::value) { + quotedNames.push_back(quote_identifier(alias_extractor<T>::get()) + "." + + quote_identifier(column.name)); + } else { + quotedNames.push_back(quote_identifier(column.name)); + } + }); + return quotedNames; + } else if(std::is_base_of<alias_tag, T>::value) { + return {quote_identifier(alias_extractor<T>::get()) + ".*"}; + } else { + return {"*"}; + } } template<class T> struct column_names_getter<std::reference_wrapper<T>, void> { using expression_type = std::reference_wrapper<T>; - template<class C> - std::vector<std::string> operator()(const expression_type& expression, const C& context) { + template<class Ctx> + std::vector<std::string> operator()(const expression_type& expression, const Ctx& context) const { return get_column_names(expression.get(), context); } }; template<class T> - struct column_names_getter<asterisk_t<T>, void> { + struct column_names_getter<asterisk_t<T>> { using expression_type = asterisk_t<T>; - template<class C> - std::vector<std::string> operator()(const expression_type&, const C&) { - std::vector<std::string> res; - res.push_back("*"); - return res; + template<class Ctx> + std::vector<std::string> operator()(const expression_type& expression, const Ctx& context) const { + return collect_table_column_names<T>(expression.defined_order, context); } }; @@ -13028,11 +14925,9 @@ namespace sqlite_orm { struct column_names_getter<object_t<T>, void> { using expression_type = object_t<T>; - template<class C> - std::vector<std::string> operator()(const expression_type&, const C&) { - std::vector<std::string> res; - res.push_back("*"); - return res; + template<class Ctx> + std::vector<std::string> operator()(const expression_type& expression, const Ctx& context) const { + return collect_table_column_names<T>(expression.defined_order, context); } }; @@ -13040,19 +14935,14 @@ namespace sqlite_orm { struct column_names_getter<columns_t<Args...>, void> { using expression_type = columns_t<Args...>; - template<class C> - std::vector<std::string> operator()(const expression_type& cols, const C& context) { + template<class Ctx> + std::vector<std::string> operator()(const expression_type& cols, const Ctx& context) const { std::vector<std::string> columnNames; columnNames.reserve(static_cast<size_t>(cols.count)); auto newContext = context; newContext.skip_table_name = false; iterate_tuple(cols.columns, [&columnNames, &newContext](auto& m) { - auto columnName = serialize(m, newContext); - if(columnName.length()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } + columnNames.push_back(serialize(m, newContext)); }); return columnNames; } @@ -13061,10 +14951,9 @@ namespace sqlite_orm { } } -// #include "order_by_serializator.h" +// #include "order_by_serializer.h" #include <string> // std::string -#include <vector> // std::vector #include <sstream> // std::stringstream namespace sqlite_orm { @@ -13072,76 +14961,68 @@ namespace sqlite_orm { namespace internal { template<class T, class SFINAE = void> - struct order_by_serializator; + struct order_by_serializer; - template<class T, class C> - std::string serialize_order_by(const T& t, const C& context) { - order_by_serializator<T> serializator; - return serializator(t, context); + template<class T, class Ctx> + std::string serialize_order_by(const T& t, const Ctx& context) { + order_by_serializer<T> serializer; + return serializer(t, context); } template<class O> - struct order_by_serializator<order_by_t<O>, void> { + struct order_by_serializer<order_by_t<O>, void> { using statement_type = order_by_t<O>; - template<class C> - std::string operator()(const statement_type& orderBy, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - auto columnName = serialize(orderBy.expression, newContext); - ss << columnName << " "; - if(orderBy._collate_argument.length()) { - ss << "COLLATE " << orderBy._collate_argument << " "; + + ss << serialize(orderBy.expression, newContext); + if(!orderBy._collate_argument.empty()) { + ss << " COLLATE " << orderBy._collate_argument; } switch(orderBy.asc_desc) { case 1: - ss << "ASC"; + ss << " ASC"; break; case -1: - ss << "DESC"; + ss << " DESC"; break; } return ss.str(); } }; - template<class S> - struct order_by_serializator<dynamic_order_by_t<S>, void> { - using statement_type = dynamic_order_by_t<S>; + template<class C> + struct order_by_serializer<dynamic_order_by_t<C>, void> { + using statement_type = dynamic_order_by_t<C>; - template<class C> - std::string operator()(const statement_type& orderBy, const C&) const { - std::vector<std::string> expressions; - for(auto& entry: orderBy) { - std::string entryString; - { - std::stringstream ss; - ss << entry.name << " "; - if(!entry._collate_argument.empty()) { - ss << "COLLATE " << entry._collate_argument << " "; - } - switch(entry.asc_desc) { - case 1: - ss << "ASC"; - break; - case -1: - ss << "DESC"; - break; - } - entryString = ss.str(); - } - expressions.push_back(move(entryString)); - }; + template<class Ctx> + std::string operator()(const statement_type& orderBy, const Ctx&) const { std::stringstream ss; ss << static_cast<std::string>(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { + int index = 0; + for(const dynamic_order_by_entry_t& entry: orderBy) { + if(index > 0) { ss << ", "; } - } - ss << " "; + + ss << entry.name; + if(!entry._collate_argument.empty()) { + ss << " COLLATE " << entry._collate_argument; + } + switch(entry.asc_desc) { + case 1: + ss << " ASC"; + break; + case -1: + ss << " DESC"; + break; + } + ++index; + }; return ss.str(); } }; @@ -13149,122 +15030,198 @@ namespace sqlite_orm { } } -// #include "values.h" +// #include "serializing_util.h" -// #include "table_type.h" +// #include "statement_binder.h" -// #include "indexed_column.h" +// #include "values.h" -// #include "function.h" +// #include "triggers.h" -// #include "ast/upsert_clause.h" +// #include "table_type_of.h" -// #include "ast/excluded.h" +// #include "index.h" + +// #include "util.h" namespace sqlite_orm { namespace internal { template<class T, class SFINAE = void> - struct statement_serializator; + struct statement_serializer; - template<class T, class C> - std::string serialize(const T& t, const C& context) { - statement_serializator<T> serializator; - return serializator(t, context); + template<class T, class I> + std::string serialize(const T& t, const serializer_context<I>& context) { + statement_serializer<T> serializer; + return serializer(t, context); } + /** + * Serializer for bindable types. + */ template<class T> - struct statement_serializator<T, typename std::enable_if<is_bindable<T>::value>::type> { + struct statement_serializer<T, match_if<is_bindable, T>> { using statement_type = T; - template<class C> - std::string operator()(const statement_type& statement, const C& context) { + template<class Ctx> + std::string operator()(const T& statement, const Ctx& context) const { if(context.replace_bindable_with_question) { return "?"; } else { - return field_printer<T>{}(statement); + return this->do_serialize(statement); } } + + private: + template<class X, + std::enable_if_t<is_printable_v<X> && !std::is_base_of<std::string, X>::value +#ifndef SQLITE_ORM_OMITS_CODECVT + && !std::is_base_of<std::wstring, X>::value +#endif + , + bool> = true> + std::string do_serialize(const X& c) const { + static_assert(std::is_same<X, T>::value, ""); + + // implementation detail: utilizing field_printer + return field_printer<X>{}(c); + } + + std::string do_serialize(const std::string& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer<std::string>{}(c)); + } + + std::string do_serialize(const char* c) const { + return quote_string_literal(c); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring& c) const { + // implementation detail: utilizing field_printer + return quote_string_literal(field_printer<std::wstring>{}(c)); + } + + std::string do_serialize(const wchar_t* c) const { + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; + return quote_string_literal(converter.to_bytes(c)); + } +#endif +#ifdef SQLITE_ORM_STRING_VIEW_SUPPORTED + std::string do_serialize(const std::string_view& c) const { + return quote_string_literal(std::string(c)); + } +#ifndef SQLITE_ORM_OMITS_CODECVT + std::string do_serialize(const std::wstring_view& c) const { + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter; + return quote_string_literal(converter.to_bytes(c.data(), c.data() + c.size())); + } +#endif +#endif + /** + * Specialization for binary data (std::vector<char>). + */ + std::string do_serialize(const std::vector<char>& t) const { + return quote_blob_literal(field_printer<std::vector<char>>{}(t)); + } + + template<class P, class PT, class D> + std::string do_serialize(const pointer_binding<P, PT, D>&) const { + // always serialize null (security reasons) + return field_printer<nullptr_t>{}(nullptr); + } }; + /** + * Serializer for literal values. + */ template<class T> - struct statement_serializator<excluded_t<T>, void> { + struct statement_serializer<T, internal::match_specialization_of<T, literal_holder>> { + using statement_type = T; + + template<class Ctx> + std::string operator()(const T& literal, const Ctx& context) const { + static_assert(is_bindable_v<type_t<T>>, "A literal value must be also bindable"); + + Ctx literalCtx = context; + literalCtx.replace_bindable_with_question = false; + statement_serializer<type_t<T>> serializer{}; + return serializer(literal.value, literalCtx); + } + }; + + template<class F, class W> + struct statement_serializer<filtered_aggregate_function<F, W>, void> { + using statement_type = filtered_aggregate_function<F, W>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) { + std::stringstream ss; + ss << serialize(statement.function, context); + ss << " FILTER (WHERE " << serialize(statement.where, context) << ")"; + return ss.str(); + } + }; + + template<class T> + struct statement_serializer<excluded_t<T>, void> { using statement_type = excluded_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << "excluded."; - if(auto columnNamePointer = context.impl.column_name(statement.expression)) { - ss << "\"" << *columnNamePointer << "\""; + if(auto* columnName = find_column_name(context.db_objects, statement.expression)) { + ss << streaming_identifier(*columnName); } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } return ss.str(); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct statement_serializator<as_optional_t<T>, void> { + struct statement_serializer<as_optional_t<T>, void> { using statement_type = as_optional_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { return serialize(statement.value, context); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct statement_serializator<std::reference_wrapper<T>, void> { + struct statement_serializer<std::reference_wrapper<T>, void> { using statement_type = std::reference_wrapper<T>; - template<class C> - std::string operator()(const statement_type& s, const C& context) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx& context) const { return serialize(s.get(), context); } }; - template<> - struct statement_serializator<std::nullptr_t, void> { - using statement_type = std::nullptr_t; - - template<class C> - std::string operator()(const statement_type&, const C&) { - return "?"; - } - }; -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED - template<> - struct statement_serializator<std::nullopt_t, void> { - using statement_type = std::nullopt_t; - - template<class C> - std::string operator()(const statement_type&, const C&) { - return "?"; - } - }; -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct statement_serializator<alias_holder<T>, void> { + struct statement_serializer<alias_holder<T>, void> { using statement_type = alias_holder<T>; - template<class C> - std::string operator()(const statement_type&, const C&) { - return T::get(); + template<class Ctx> + std::string operator()(const statement_type&, const Ctx&) { + std::stringstream ss; + ss << streaming_identifier(T::get()); + return ss.str(); } }; template<class... TargetArgs, class... ActionsArgs> - struct statement_serializator<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> { + struct statement_serializer<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> { using statement_type = upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << "ON CONFLICT"; iterate_tuple(statement.target_args, [&ss, &context](auto& value) { - using value_type = typename std::decay<decltype(value)>::type; + using value_type = std::decay_t<decltype(value)>; auto needParenthesis = std::is_member_pointer<value_type>::value; ss << ' '; if(needParenthesis) { @@ -13279,86 +15236,69 @@ namespace sqlite_orm { if(std::tuple_size<typename statement_type::actions_tuple>::value == 0) { ss << " NOTHING"; } else { - ss << " UPDATE"; auto updateContext = context; updateContext.use_parentheses = false; - iterate_tuple(statement.actions, [&ss, &updateContext](auto& value) { - ss << ' ' << serialize(value, updateContext); - }); + ss << " UPDATE " << streaming_actions_tuple(statement.actions, updateContext); } return ss.str(); } }; template<class R, class S, class... Args> - struct statement_serializator<built_in_function_t<R, S, Args...>, void> { + struct statement_serializer<built_in_function_t<R, S, Args...>, void> { using statement_type = built_in_function_t<R, S, Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << statement.serialize() << "("; - std::vector<std::string> args; - using args_type = typename std::decay<decltype(statement)>::type::args_type; - args.reserve(std::tuple_size<args_type>::value); - iterate_tuple(statement.args, [&args, &context](auto& v) { - args.push_back(serialize(v, context)); - }); - for(size_t i = 0; i < args.size(); ++i) { - ss << args[i]; - if(i < args.size() - 1) { - ss << ", "; - } + if(context.use_parentheses) { + ss << '('; + } + ss << statement.serialize() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; + if(context.use_parentheses) { + ss << ')'; } - ss << ")"; return ss.str(); } }; + template<class R, class S, class... Args> + struct statement_serializer<built_in_aggregate_function_t<R, S, Args...>, void> + : statement_serializer<built_in_function_t<R, S, Args...>, void> {}; + template<class F, class... Args> - struct statement_serializator<function_call<F, Args...>, void> { + struct statement_serializer<function_call<F, Args...>, void> { using statement_type = function_call<F, Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { - using args_tuple = std::tuple<Args...>; - + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << F::name() << "("; - auto index = 0; - iterate_tuple(statement.args, [&context, &ss, &index](auto& v) { - auto value = serialize(v, context); - ss << value; - if(index < std::tuple_size<args_tuple>::value - 1) { - ss << ", "; - } - ++index; - }); - ss << ")"; + ss << F::name() << "(" << streaming_expressions_tuple(statement.args, context) << ")"; return ss.str(); } }; template<class T, class E> - struct statement_serializator<as_t<T, E>, void> { + struct statement_serializer<as_t<T, E>, void> { using statement_type = as_t<T, E>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { - auto tableAliasString = alias_extractor<T>::get(); - return serialize(c.expression, context) + " AS " + tableAliasString; + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << serialize(c.expression, context) + " AS " << streaming_identifier(alias_extractor<T>::get()); + return ss.str(); } }; template<class T, class P> - struct statement_serializator<alias_column_t<T, P>, void> { + struct statement_serializer<alias_column_t<T, P>, void> { using statement_type = alias_column_t<T, P>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << T::get() << "'."; + ss << streaming_identifier(alias_extractor<T>::get()) << "."; } auto newContext = context; newContext.skip_table_name = true; @@ -13367,92 +15307,64 @@ namespace sqlite_orm { } }; - template<> - struct statement_serializator<std::string, void> { - using statement_type = std::string; - - template<class C> - std::string operator()(const statement_type& c, const C& context) const { - if(context.replace_bindable_with_question) { - return "?"; - } else { - return "\'" + c + "\'"; - } - } - }; - - template<> - struct statement_serializator<const char*, void> { - using statement_type = const char*; - - template<class C> - std::string operator()(const char* c, const C& context) const { - if(context.replace_bindable_with_question) { - return "?"; - } else { - return std::string("'") + c + "'"; - } - } - }; - template<class O, class F> - struct statement_serializator<F O::*, void> { + struct statement_serializer<F O::*, void> { using statement_type = F O::*; - template<class C> - std::string operator()(const statement_type& m, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& m, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "\"" << context.impl.find_table_name(typeid(O)) << "\"."; + ss << streaming_identifier(lookup_table_name<O>(context.db_objects)) << "."; } - if(auto columnnamePointer = context.column_name(m)) { - ss << "\"" << *columnnamePointer << "\""; + if(auto* columnName = find_column_name(context.db_objects, m)) { + ss << streaming_identifier(*columnName); } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } return ss.str(); } }; template<> - struct statement_serializator<rowid_t, void> { + struct statement_serializer<rowid_t, void> { using statement_type = rowid_t; - template<class C> - std::string operator()(const statement_type& s, const C&) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx&) const { return static_cast<std::string>(s); } }; template<> - struct statement_serializator<oid_t, void> { + struct statement_serializer<oid_t, void> { using statement_type = oid_t; - template<class C> - std::string operator()(const statement_type& s, const C&) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx&) const { return static_cast<std::string>(s); } }; template<> - struct statement_serializator<_rowid_t, void> { + struct statement_serializer<_rowid_t, void> { using statement_type = _rowid_t; - template<class C> - std::string operator()(const statement_type& s, const C&) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx&) const { return static_cast<std::string>(s); } }; template<class O> - struct statement_serializator<table_rowid_t<O>, void> { + struct statement_serializer<table_rowid_t<O>, void> { using statement_type = table_rowid_t<O>; - template<class C> - std::string operator()(const statement_type& s, const C& context) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + ss << streaming_identifier(lookup_table_name<O>(context.db_objects)) << "."; } ss << static_cast<std::string>(s); return ss.str(); @@ -13460,14 +15372,14 @@ namespace sqlite_orm { }; template<class O> - struct statement_serializator<table_oid_t<O>, void> { + struct statement_serializer<table_oid_t<O>, void> { using statement_type = table_oid_t<O>; - template<class C> - std::string operator()(const statement_type& s, const C& context) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + ss << streaming_identifier(lookup_table_name<O>(context.db_objects)) << "."; } ss << static_cast<std::string>(s); return ss.str(); @@ -13475,14 +15387,14 @@ namespace sqlite_orm { }; template<class O> - struct statement_serializator<table__rowid_t<O>, void> { + struct statement_serializer<table__rowid_t<O>, void> { using statement_type = table__rowid_t<O>; - template<class C> - std::string operator()(const statement_type& s, const C& context) { + template<class Ctx> + std::string operator()(const statement_type& s, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(O)) << "'."; + ss << streaming_identifier(lookup_table_name<O>(context.db_objects)) << "."; } ss << static_cast<std::string>(s); return ss.str(); @@ -13490,18 +15402,18 @@ namespace sqlite_orm { }; template<class L, class R, class... Ds> - struct statement_serializator<binary_operator<L, R, Ds...>, void> { + struct statement_serializer<binary_operator<L, R, Ds...>, void> { using statement_type = binary_operator<L, R, Ds...>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { - auto lhs = serialize(c.lhs, context); - auto rhs = serialize(c.rhs, context); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto lhs = serialize(statement.lhs, context); + auto rhs = serialize(statement.rhs, context); std::stringstream ss; if(context.use_parentheses) { ss << '('; } - ss << lhs << " " << static_cast<std::string>(c) << " " << rhs; + ss << lhs << " " << statement.serialize() << " " << rhs; if(context.use_parentheses) { ss << ')'; } @@ -13510,21 +15422,21 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<count_asterisk_t<T>, void> { + struct statement_serializer<count_asterisk_t<T>, void> { using statement_type = count_asterisk_t<T>; - template<class C> - std::string operator()(const statement_type&, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type&, const Ctx& context) const { return serialize(count_asterisk_without_type{}, context); } }; template<> - struct statement_serializator<count_asterisk_without_type, void> { + struct statement_serializer<count_asterisk_without_type, void> { using statement_type = count_asterisk_without_type; - template<class C> - std::string operator()(const statement_type& c, const C&) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx&) const { std::stringstream ss; auto functionName = c.serialize(); ss << functionName << "(*)"; @@ -13533,11 +15445,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<distinct_t<T>, void> { + struct statement_serializer<distinct_t<T>, void> { using statement_type = distinct_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; auto expr = serialize(c.value, context); ss << static_cast<std::string>(c) << "(" << expr << ")"; @@ -13546,11 +15458,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<all_t<T>, void> { + struct statement_serializer<all_t<T>, void> { using statement_type = all_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; auto expr = serialize(c.value, context); ss << static_cast<std::string>(c) << "(" << expr << ")"; @@ -13559,30 +15471,30 @@ namespace sqlite_orm { }; template<class T, class F> - struct statement_serializator<column_pointer<T, F>, void> { + struct statement_serializer<column_pointer<T, F>, void> { using statement_type = column_pointer<T, F>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; if(!context.skip_table_name) { - ss << "'" << context.impl.find_table_name(typeid(T)) << "'."; + ss << streaming_identifier(lookup_table_name<T>(context.db_objects)) << "."; } - if(auto columnNamePointer = context.impl.column_name_simple(c.field)) { - ss << "\"" << *columnNamePointer << "\""; + if(auto* columnName = find_column_name(context.db_objects, c)) { + ss << streaming_identifier(*columnName); } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); + throw std::system_error{orm_error_code::column_not_found}; } return ss.str(); } }; template<class T, class E> - struct statement_serializator<cast_t<T, E>, void> { + struct statement_serializer<cast_t<T, E>, void> { using statement_type = cast_t<T, E>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " ("; ss << serialize(c.expression, context) << " AS " << type_printer<T>().print() << ")"; @@ -13591,12 +15503,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<T, - typename std::enable_if<is_base_of_template<T, compound_operator>::value>::type> { + struct statement_serializer<T, std::enable_if_t<is_base_of_template_v<T, compound_operator>>> { using statement_type = T; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.left, context) << " "; ss << static_cast<std::string>(c) << " "; @@ -13606,11 +15517,11 @@ namespace sqlite_orm { }; template<class R, class T, class E, class... Args> - struct statement_serializator<simple_case_t<R, T, E, Args...>, void> { + struct statement_serializer<simple_case_t<R, T, E, Args...>, void> { using statement_type = simple_case_t<R, T, E, Args...>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << "CASE "; c.case_expression.apply([&ss, context](auto& c_) { @@ -13629,11 +15540,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<is_null_t<T>, void> { + struct statement_serializer<is_null_t<T>, void> { using statement_type = is_null_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast<std::string>(c); return ss.str(); @@ -13641,11 +15552,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<is_not_null_t<T>, void> { + struct statement_serializer<is_not_null_t<T>, void> { using statement_type = is_not_null_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.t, context) << " " << static_cast<std::string>(c); return ss.str(); @@ -13653,25 +15564,25 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<bitwise_not_t<T>, void> { + struct statement_serializer<bitwise_not_t<T>, void> { using statement_type = bitwise_not_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(c) << " "; - auto cString = serialize(c.argument, context); + ss << statement.serialize() << " "; + auto cString = serialize(statement.argument, context); ss << " (" << cString << " )"; return ss.str(); } }; template<class T> - struct statement_serializator<negated_condition_t<T>, void> { + struct statement_serializer<negated_condition_t<T>, void> { using statement_type = negated_condition_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << static_cast<std::string>(c) << " "; auto cString = serialize(c.c, context); @@ -13681,12 +15592,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<T, - typename std::enable_if<is_base_of_template<T, binary_condition>::value>::type> { + struct statement_serializer<T, std::enable_if_t<is_base_of_template_v<T, binary_condition>>> { using statement_type = T; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { auto leftString = serialize(c.l, context); auto rightString = serialize(c.r, context); std::stringstream ss; @@ -13702,11 +15612,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<named_collate<T>, void> { + struct statement_serializer<named_collate<T>, void> { using statement_type = named_collate<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -13715,11 +15625,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<collate_t<T>, void> { + struct statement_serializer<collate_t<T>, void> { using statement_type = collate_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { auto newContext = context; newContext.use_parentheses = false; auto res = serialize(c.expr, newContext); @@ -13728,74 +15638,88 @@ namespace sqlite_orm { }; template<class L, class A> - struct statement_serializator<dynamic_in_t<L, A>, void> { + struct statement_serializer<dynamic_in_t<L, A>, void> { using statement_type = dynamic_in_t<L, A>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto leftString = serialize(c.left, context); - ss << leftString << " " << static_cast<std::string>(c) << " "; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; + constexpr bool isCompoundOperator = is_base_of_template_v<A, compound_operator>; + if(isCompoundOperator) { + ss << '('; + } auto newContext = context; newContext.use_parentheses = true; - ss << serialize(c.argument, newContext); + ss << serialize(statement.argument, newContext); + if(isCompoundOperator) { + ss << ')'; + } return ss.str(); } }; template<class L, class E> - struct statement_serializator<dynamic_in_t<L, std::vector<E>>, void> { + struct statement_serializer<dynamic_in_t<L, std::vector<E>>, void> { using statement_type = dynamic_in_t<L, std::vector<E>>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto leftString = serialize(c.left, context); - ss << leftString << " " << static_cast<std::string>(c) << " ("; - for(size_t index = 0; index < c.argument.size(); ++index) { - auto& value = c.argument[index]; - ss << serialize(value, context); - if(index < c.argument.size() - 1) { - ss << ", "; - } + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; } - ss << ")"; + ss << " (" << streaming_dynamic_expressions(statement.argument, context) << ")"; return ss.str(); } }; template<class L, class... Args> - struct statement_serializator<in_t<L, Args...>, void> { + struct statement_serializer<in_t<L, Args...>, void> { using statement_type = in_t<L, Args...>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto leftString = serialize(c.left, context); - ss << leftString << " " << static_cast<std::string>(c) << " ("; - std::vector<std::string> args; + auto leftString = serialize(statement.left, context); + ss << leftString << " "; + if(!statement.negative) { + ss << "IN"; + } else { + ss << "NOT IN"; + } + ss << " "; using args_type = std::tuple<Args...>; - args.reserve(std::tuple_size<args_type>::value); - iterate_tuple(c.argument, [&args, &context](auto& v) { - args.push_back(serialize(v, context)); - }); - for(size_t i = 0; i < args.size(); ++i) { - ss << args[i]; - if(i < args.size() - 1) { - ss << ", "; - } + constexpr bool theOnlySelect = + std::tuple_size<args_type>::value == 1 && is_select_v<std::tuple_element_t<0, args_type>>; + if(!theOnlySelect) { + ss << "("; + } + ss << streaming_expressions_tuple(statement.argument, context); + if(!theOnlySelect) { + ss << ")"; } - ss << ")"; return ss.str(); } }; template<class A, class T, class E> - struct statement_serializator<like_t<A, T, E>, void> { + struct statement_serializer<like_t<A, T, E>, void> { using statement_type = like_t<A, T, E>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast<std::string>(c) << " "; @@ -13808,11 +15732,11 @@ namespace sqlite_orm { }; template<class A, class T> - struct statement_serializator<glob_t<A, T>, void> { + struct statement_serializer<glob_t<A, T>, void> { using statement_type = glob_t<A, T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; ss << serialize(c.arg, context) << " "; ss << static_cast<std::string>(c) << " "; @@ -13822,11 +15746,11 @@ namespace sqlite_orm { }; template<class A, class T> - struct statement_serializator<between_t<A, T>, void> { + struct statement_serializer<between_t<A, T>, void> { using statement_type = between_t<A, T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; auto expr = serialize(c.expr, context); ss << expr << " " << static_cast<std::string>(c) << " "; @@ -13838,157 +15762,143 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<exists_t<T>, void> { + struct statement_serializer<exists_t<T>, void> { using statement_type = exists_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(c) << " "; - ss << serialize(c.t, context); + ss << "EXISTS "; + ss << serialize(statement.expression, context); return ss.str(); } }; template<> - struct statement_serializator<autoincrement_t, void> { + struct statement_serializer<autoincrement_t, void> { using statement_type = autoincrement_t; - template<class C> - std::string operator()(const statement_type& c, const C&) const { - return static_cast<std::string>(c); + template<class Ctx> + std::string operator()(const statement_type&, const Ctx&) const { + return "AUTOINCREMENT"; + } + }; + + template<> + struct statement_serializer<conflict_clause_t, void> { + using statement_type = conflict_clause_t; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case conflict_clause_t::rollback: + return "ROLLBACK"; + case conflict_clause_t::abort: + return "ABORT"; + case conflict_clause_t::fail: + return "FAIL"; + case conflict_clause_t::ignore: + return "IGNORE"; + case conflict_clause_t::replace: + return "REPLACE"; + } + return {}; + } + }; + + template<class T> + struct statement_serializer<primary_key_with_autoincrement<T>, void> { + using statement_type = primary_key_with_autoincrement<T>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + return serialize(statement.primary_key, context) + " AUTOINCREMENT"; } }; template<class... Cs> - struct statement_serializator<primary_key_t<Cs...>, void> { + struct statement_serializer<primary_key_t<Cs...>, void> { using statement_type = primary_key_t<Cs...>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { - auto res = static_cast<std::string>(c); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "PRIMARY KEY"; + switch(statement.options.asc_option) { + case statement_type::order_by::ascending: + ss << " ASC"; + break; + case statement_type::order_by::descending: + ss << " DESC"; + break; + default: + break; + } + if(statement.options.conflict_clause_is_on) { + ss << " ON CONFLICT " << serialize(statement.options.conflict_clause, context); + } using columns_tuple = typename statement_type::columns_tuple; - auto columnsCount = std::tuple_size<columns_tuple>::value; + const size_t columnsCount = std::tuple_size<columns_tuple>::value; if(columnsCount) { - res += "("; - decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { - if(auto columnNamePointer = context.column_name(column)) { - res += *columnNamePointer; - if(columnIndex < columnsCount - 1) { - res += ", "; - } - ++columnIndex; - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - res += ")"; + ss << "(" << streaming_mapped_columns_expressions(statement.columns, context) << ")"; } - return res; + return ss.str(); } }; template<class... Args> - struct statement_serializator<unique_t<Args...>, void> { + struct statement_serializer<unique_t<Args...>, void> { using statement_type = unique_t<Args...>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { - auto res = static_cast<std::string>(c); + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { + std::stringstream ss; + ss << static_cast<std::string>(c); using columns_tuple = typename statement_type::columns_tuple; - auto columnsCount = std::tuple_size<columns_tuple>::value; + const size_t columnsCount = std::tuple_size<columns_tuple>::value; if(columnsCount) { - res += "("; - decltype(columnsCount) columnIndex = 0; - iterate_tuple(c.columns, [&context, &res, &columnIndex, columnsCount](auto& column) { - if(auto columnNamePointer = context.column_name(column)) { - res += *columnNamePointer; - if(columnIndex < columnsCount - 1) { - res += ", "; - } - ++columnIndex; - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - res += ")"; + ss << "(" << streaming_mapped_columns_expressions(c.columns, context) << ")"; } - return res; + return ss.str(); } }; template<> - struct statement_serializator<collate_constraint_t, void> { + struct statement_serializer<collate_constraint_t, void> { using statement_type = collate_constraint_t; - template<class C> - std::string operator()(const statement_type& c, const C&) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx&) const { return static_cast<std::string>(c); } }; template<class T> - struct statement_serializator<default_t<T>, void> { + struct statement_serializer<default_t<T>, void> { using statement_type = default_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { return static_cast<std::string>(c) + " (" + serialize(c.value, context) + ")"; } }; template<class... Cs, class... Rs> - struct statement_serializator<foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>, void> { + struct statement_serializer<foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>, void> { using statement_type = foreign_key_t<std::tuple<Cs...>, std::tuple<Rs...>>; - template<class C> - std::string operator()(const statement_type& fk, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& fk, const Ctx& context) const { std::stringstream ss; - std::vector<std::string> columnNames; - using columns_type_t = typename std::decay<decltype(fk)>::type::columns_type; - constexpr const size_t columnsCount = std::tuple_size<columns_type_t>::value; - columnNames.reserve(columnsCount); - iterate_tuple(fk.columns, [&columnNames, &context](auto& v) { - if(auto columnNamePointer = context.impl.column_name(v)) { - columnNames.push_back(*columnNamePointer); - } else { - columnNames.push_back({}); - } - }); - ss << "FOREIGN KEY("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "'" << columnNames[i] << "'"; - if(i < columnNames.size() - 1) { - ss << ", "; - } - } - ss << ") REFERENCES "; - std::vector<std::string> referencesNames; - using references_type_t = typename std::decay<decltype(fk)>::type::references_type; - constexpr const size_t referencesCount = std::tuple_size<references_type_t>::value; - referencesNames.reserve(referencesCount); + ss << "FOREIGN KEY(" << streaming_mapped_columns_expressions(fk.columns, context) << ") REFERENCES "; { - using first_reference_t = typename std::tuple_element<0, references_type_t>::type; - using first_reference_mapped_type = typename internal::table_type<first_reference_t>::type; - auto refTableName = context.impl.find_table_name(typeid(first_reference_mapped_type)); - ss << '\'' << refTableName << '\''; + using references_type_t = typename std::decay_t<decltype(fk)>::references_type; + using first_reference_t = std::tuple_element_t<0, references_type_t>; + using first_reference_mapped_type = table_type_of_t<first_reference_t>; + auto refTableName = lookup_table_name<first_reference_mapped_type>(context.db_objects); + ss << streaming_identifier(refTableName); } - iterate_tuple(fk.references, [&referencesNames, &context](auto& v) { - if(auto columnNamePointer = context.impl.column_name(v)) { - referencesNames.push_back(*columnNamePointer); - } else { - referencesNames.push_back({}); - } - }); - ss << "("; - for(size_t i = 0; i < referencesNames.size(); ++i) { - ss << "'" << referencesNames[i] << "'"; - if(i < referencesNames.size() - 1) { - ss << ", "; - } - } - ss << ")"; + ss << "(" << streaming_mapped_columns_expressions(fk.references, context) << ")"; if(fk.on_update) { ss << ' ' << static_cast<std::string>(fk.on_update) << " " << fk.on_update._action; } @@ -14000,354 +15910,294 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<check_t<T>, void> { + struct statement_serializer<check_t<T>, void> { using statement_type = check_t<T>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { - return static_cast<std::string>(c) + " " + serialize(c.expression, context); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CHECK (" << serialize(statement.expression, context) << ")"; + return ss.str(); } }; +#if SQLITE_VERSION_NUMBER >= 3031000 + template<class T> + struct statement_serializer<generated_always_t<T>, void> { + using statement_type = generated_always_t<T>; - template<class O, class T, class G, class S, class... Op> - struct statement_serializator<column_t<O, T, G, S, Op...>, void> { - using statement_type = column_t<O, T, G, S, Op...>; - - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << "'" << c.name << "' "; - using column_type = typename std::decay<decltype(c)>::type; - using field_type = typename column_type::field_type; - using constraints_type = typename column_type::constraints_type; - ss << type_printer<field_type>().print() << " "; - { - std::vector<std::string> constraintsStrings; - constexpr const size_t constraintsCount = std::tuple_size<constraints_type>::value; - constraintsStrings.reserve(constraintsCount); - int primaryKeyIndex = -1; - int autoincrementIndex = -1; - int tupleIndex = 0; - iterate_tuple( - c.constraints, - [&constraintsStrings, &primaryKeyIndex, &autoincrementIndex, &tupleIndex, &context](auto& v) { - using constraint_type = typename std::decay<decltype(v)>::type; - constraintsStrings.push_back(serialize(v, context)); - if(is_primary_key<constraint_type>::value) { - primaryKeyIndex = tupleIndex; - } else if(std::is_same<autoincrement_t, constraint_type>::value) { - autoincrementIndex = tupleIndex; - } - ++tupleIndex; - }); - if(primaryKeyIndex != -1 && autoincrementIndex != -1 && autoincrementIndex < primaryKeyIndex) { - iter_swap(constraintsStrings.begin() + primaryKeyIndex, - constraintsStrings.begin() + autoincrementIndex); - } - for(auto& str: constraintsStrings) { - ss << str << ' '; - } + if(statement.full) { + ss << "GENERATED ALWAYS "; } - if(c.not_null()) { - ss << "NOT NULL "; + ss << "AS ("; + ss << serialize(statement.expression, context) << ")"; + switch(statement.storage) { + case basic_generated_always::storage_type::not_specified: + //.. + break; + case basic_generated_always::storage_type::virtual_: + ss << " VIRTUAL"; + break; + case basic_generated_always::storage_type::stored: + ss << " STORED"; + break; } return ss.str(); } }; +#endif + template<class G, class S, class... Op> + struct statement_serializer<column_t<G, S, Op...>, void> { + using statement_type = column_t<G, S, Op...>; + + template<class Ctx> + std::string operator()(const statement_type& column, const Ctx& context) const { + using column_type = statement_type; + + std::stringstream ss; + ss << streaming_identifier(column.name) << " " << type_printer<field_type_t<column_type>>().print() + << " " + << streaming_column_constraints( + call_as_template_base<column_constraints>(polyfill::identity{})(column), + column.is_not_null(), + context); + return ss.str(); + } + }; template<class T, class... Args> - struct statement_serializator<remove_all_t<T, Args...>, void> { + struct statement_serializer<remove_all_t<T, Args...>, void> { using statement_type = remove_all_t<T, Args...>; - template<class C> - std::string operator()(const statement_type& rem, const C& context) const { - auto& tImpl = context.impl.template get_impl<T>(); + template<class Ctx> + std::string operator()(const statement_type& rem, const Ctx& context) const { + auto& table = pick_table<T>(context.db_objects); + std::stringstream ss; - ss << "DELETE FROM '" << tImpl.table.name << "' "; - iterate_tuple(rem.conditions, [&context, &ss](auto& v) { - ss << serialize(v, context); - }); + ss << "DELETE FROM " << streaming_identifier(table.name) + << streaming_conditions_tuple(rem.conditions, context); return ss.str(); } }; template<class T> - struct statement_serializator<replace_t<T>, void> { + struct statement_serializer<replace_t<T>, void> { using statement_type = replace_t<T>; - template<class C> - std::string operator()(const statement_type& rep, const C& context) const { - return serialize_replace_range_impl(rep, context, 1); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t<decltype(statement)>; + using object_type = typename expression_object_type<expression_type>::type; + auto& table = pick_table<object_type>(context.db_objects); + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")" + << " VALUES (" + << streaming_field_values_excluding(check_if<is_generated_always>{}, + empty_callable<std::false_type>(), // don't exclude + context, + get_ref(statement.object)) + << ")"; + return ss.str(); } }; template<class T, class... Cols> - struct statement_serializator<insert_explicit<T, Cols...>, void> { + struct statement_serializer<insert_explicit<T, Cols...>, void> { using statement_type = insert_explicit<T, Cols...>; - template<class C> - std::string operator()(const statement_type& ins, const C& context) const { - constexpr const size_t colsCount = std::tuple_size<std::tuple<Cols...>>::value; + template<class Ctx> + std::string operator()(const statement_type& ins, const Ctx& context) const { + constexpr size_t colsCount = std::tuple_size<std::tuple<Cols...>>::value; static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); - using expression_type = typename std::decay<decltype(ins)>::type; + using expression_type = std::decay_t<decltype(ins)>; using object_type = typename expression_object_type<expression_type>::type; - auto& tImpl = context.impl.template get_impl<object_type>(); + auto& table = pick_table<object_type>(context.db_objects); std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector<std::string> columnNames; - columnNames.reserve(colsCount); - { - auto columnsContext = context; - columnsContext.skip_table_name = true; - iterate_tuple(ins.columns.columns, [&columnNames, &columnsContext](auto& m) { - auto columnName = serialize(m, columnsContext); - if(!columnName.empty()) { - columnNames.push_back(columnName); - } else { - throw std::system_error(std::make_error_code(orm_error_code::column_not_found)); - } - }); - } - ss << "("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - ss << "VALUES ("; - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << "?"; - if(i < columnNames.size() - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + ss << "(" << streaming_mapped_columns_expressions(ins.columns.columns, context) << ") " + << "VALUES ("; + iterate_tuple(ins.columns.columns, + [&ss, &context, &object = get_ref(ins.obj), first = true](auto& memberPointer) mutable { + using member_pointer_type = std::decay_t<decltype(memberPointer)>; + static_assert(!is_setter_v<member_pointer_type>, + "Unable to use setter within insert explicit"); + + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << serialize(polyfill::invoke(memberPointer, object), context); + }); + ss << ")"; return ss.str(); } }; template<class T> - struct statement_serializator<update_t<T>, void> { + struct statement_serializer<update_t<T>, void> { using statement_type = update_t<T>; - template<class C> - std::string operator()(const statement_type& upd, const C& context) const { - using expression_type = typename std::decay<decltype(upd)>::type; + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + using expression_type = std::decay_t<decltype(statement)>; using object_type = typename expression_object_type<expression_type>::type; - auto& tImpl = context.impl.template get_impl<object_type>(); + auto& table = pick_table<object_type>(context.db_objects); std::stringstream ss; - ss << "UPDATE '" << tImpl.table.name << "' SET"; - std::vector<std::string> setColumnNames; - tImpl.table.for_each_column([&setColumnNames, &tImpl](auto& column) { - if(!column.template has<primary_key_t<>>() && - !tImpl.table.exists_in_composite_primary_key(column)) { - setColumnNames.emplace_back(column.name); - } - }); - for(size_t i = 0; i < setColumnNames.size(); ++i) { - ss << " \"" << setColumnNames[i] << "\"" - << " = ?"; - if(i < setColumnNames.size() - 1) { - ss << ","; - } - } - ss << " WHERE"; - auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << " \"" << primaryKeyColumnNames[i] << "\"" - << " = ?"; - if(i < primaryKeyColumnNames.size() - 1) { - ss << " AND"; - } - } + ss << "UPDATE " << streaming_identifier(table.name) << " SET "; + table.template for_each_column_excluding<mpl::disjunction_fn<is_primary_key, is_generated_always>>( + [&table, &ss, &context, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); + ss << " WHERE "; + table.for_each_column( + [&table, &context, &ss, &object = get_ref(statement.object), first = true](auto& column) mutable { + if(!column.template is<is_primary_key>() && !table.exists_in_composite_primary_key(column)) { + return; + } + + constexpr std::array<const char*, 2> sep = {" AND ", ""}; + ss << sep[std::exchange(first, false)] << streaming_identifier(column.name) << " = " + << serialize(polyfill::invoke(column.member_pointer, object), context); + }); return ss.str(); } }; template<class... Args> - struct statement_serializator<set_t<Args...>, void> { + struct statement_serializer<set_t<Args...>, void> { using statement_type = set_t<Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(statement); - auto assignsCount = std::tuple_size<typename statement_type::assigns_type>::value; - decltype(assignsCount) assignIndex = 0; + ss << "SET "; auto leftContext = context; leftContext.skip_table_name = true; - iterate_tuple(statement.assigns, - [&ss, &context, &leftContext, &assignIndex, assignsCount](auto& value) { - ss << ' ' << serialize(value.lhs, leftContext); - ss << ' ' << static_cast<std::string>(value) << ' '; - ss << serialize(value.rhs, context); - if(assignIndex < assignsCount - 1) { - ss << ","; - } - ++assignIndex; - }); + iterate_tuple(statement.assigns, [&ss, &context, &leftContext, first = true](auto& value) mutable { + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] << serialize(value.lhs, leftContext) << ' ' + << value.serialize() << ' ' << serialize(value.rhs, context); + }); return ss.str(); } }; template<class... Args, class... Wargs> - struct statement_serializator<update_all_t<set_t<Args...>, Wargs...>, void> { + struct statement_serializer<update_all_t<set_t<Args...>, Wargs...>, void> { using statement_type = update_all_t<set_t<Args...>, Wargs...>; - template<class C> - std::string operator()(const statement_type& upd, const C& context) const { - std::stringstream ss; - ss << "UPDATE "; - table_name_collector collector([&context](std::type_index ti) { - return context.impl.find_table_name(ti); + template<class Ctx> + std::string operator()(const statement_type& upd, const Ctx& context) const { + table_name_collector collector([&context](const std::type_index& ti) { + return find_table_name(context.db_objects, ti); }); iterate_ast(upd.set.assigns, collector); - if(!collector.table_names.empty()) { - if(collector.table_names.size() == 1) { - ss << " '" << collector.table_names.begin()->first << "' "; - ss << static_cast<std::string>(upd.set) << " "; - std::vector<std::string> setPairs; - auto leftContext = context; - leftContext.skip_table_name = true; - iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { - std::stringstream sss; - sss << serialize(asgn.lhs, leftContext); - sss << " " << static_cast<std::string>(asgn) << " "; - sss << serialize(asgn.rhs, context) << " "; - setPairs.push_back(sss.str()); - }); - auto setPairsCount = setPairs.size(); - for(size_t i = 0; i < setPairsCount; ++i) { - ss << setPairs[i] << " "; - if(i < setPairsCount - 1) { - ss << ", "; - } - } - iterate_tuple(upd.conditions, [&context, &ss](auto& v) { - ss << serialize(v, context); - }); - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::too_many_tables_specified)); - } - } else { - throw std::system_error(std::make_error_code(orm_error_code::incorrect_set_fields_specified)); - } - } - }; - - template<class T, class C> - std::string serialize_insert_range_impl(const T& /*statement*/, const C& context, const int valuesCount) { - using object_type = typename expression_object_type<T>::type; - auto& tImpl = context.impl.template get_impl<object_type>(); - std::stringstream ss; - ss << "INSERT INTO '" << tImpl.table.name << "' "; - std::vector<std::string> columnNames; - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); - - tImpl.table.for_each_column([&columnNames, &compositeKeyColumnNames](auto& c) { - using table_type = typename std::decay<decltype(tImpl.table)>::type; - if(table_type::is_without_rowid || !c.template has<primary_key_t<>>()) { - auto it = find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - columnNames.emplace_back(c.name); - } + if(collector.table_names.empty()) { + throw std::system_error{orm_error_code::no_tables_specified}; } - }); - const auto columnNamesCount = columnNames.size(); - if(columnNamesCount) { - ss << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss << "\"" << columnNames[i] << "\""; - if(i < columnNamesCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ss << " "; - } - } else { - ss << "DEFAULT "; - } - ss << "VALUES "; - if(columnNamesCount) { - auto valuesString = [columnNamesCount] { - std::stringstream ss_; - ss_ << "("; - for(size_t i = 0; i < columnNamesCount; ++i) { - ss_ << "?"; - if(i < columnNamesCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; + std::stringstream ss; + ss << "UPDATE " << streaming_identifier(collector.table_names.begin()->first) << " SET "; + { + std::vector<std::string> setPairs; + setPairs.reserve(std::tuple_size<typename set_t<Args...>::assigns_type>::value); + auto leftContext = context; + leftContext.skip_table_name = true; + iterate_tuple(upd.set.assigns, [&context, &leftContext, &setPairs](auto& asgn) { + std::stringstream sss; + sss << serialize(asgn.lhs, leftContext); + sss << ' ' << asgn.serialize() << ' '; + sss << serialize(asgn.rhs, context); + setPairs.push_back(sss.str()); + }); + ss << streaming_serialized(setPairs) << streaming_conditions_tuple(upd.conditions, context); + return ss.str(); } - } else if(valuesCount != 1) { - throw std::system_error(std::make_error_code(orm_error_code::cannot_use_default_value)); } - - return ss.str(); - } + }; template<class T> - struct statement_serializator<insert_t<T>, void> { + struct statement_serializer<insert_t<T>, void> { using statement_type = insert_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { - return serialize_insert_range_impl(statement, context, 1); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type<statement_type>::type; + auto& table = pick_table<object_type>(context.db_objects); + using is_without_rowid = typename std::decay_t<decltype(table)>::is_without_rowid; + + std::vector<std::reference_wrapper<const std::string>> columnNames; + table.template for_each_column_excluding< + mpl::conjunction<mpl::not_<mpl::always<is_without_rowid>>, + mpl::disjunction_fn<is_primary_key, is_generated_always>>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES"; + if(columnNamesCount) { + ss << " (" + << streaming_field_values_excluding( + mpl::conjunction<mpl::not_<mpl::always<is_without_rowid>>, + mpl::disjunction_fn<is_primary_key, is_generated_always>>{}, + [&table](auto& column) { + return table.exists_in_composite_primary_key(column); + }, + context, + get_ref(statement.object)) + << ")"; + } + + return ss.str(); } }; template<class T> - struct statement_serializator<into_t<T>, void> { + struct statement_serializer<into_t<T>, void> { using statement_type = into_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type&, const Ctx& context) const { + auto& table = pick_table<T>(context.db_objects); + std::stringstream ss; - auto& tImpl = context.impl.template get_impl<T>(); - ss << "INTO " << tImpl.table.name; + ss << "INTO " << streaming_identifier(table.name); return ss.str(); } }; template<class... Args> - struct statement_serializator<columns_t<Args...>, void> { + struct statement_serializer<columns_t<Args...>, void> { using statement_type = columns_t<Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - auto index = 0; if(context.use_parentheses) { ss << '('; } - iterate_tuple(statement.columns, [&context, &ss, &index](auto& value) { - ss << serialize(value, context); - if(index < int(std::tuple_size<std::tuple<Args...>>::value) - 1) { - ss << ", "; - } - ++index; - }); + ss << streaming_expressions_tuple(statement.columns, context); if(context.use_parentheses) { ss << ')'; } @@ -14356,28 +16206,26 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator< - T, - typename std::enable_if<is_insert_raw<T>::value || is_replace_raw<T>::value>::type> { + struct statement_serializer<T, std::enable_if_t<polyfill::disjunction_v<is_insert_raw<T>, is_replace_raw<T>>>> { using statement_type = T; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - if(is_insert_raw<T>::value) { + if(is_insert_raw_v<T>) { ss << "INSERT"; } else { ss << "REPLACE"; } iterate_tuple(statement.args, [&context, &ss](auto& value) { - using value_type = typename std::decay<decltype(value)>::type; + using value_type = std::decay_t<decltype(value)>; ss << ' '; - if(is_columns<value_type>::value) { + if(is_columns_v<value_type>) { auto newContext = context; newContext.skip_table_name = true; newContext.use_parentheses = true; ss << serialize(value, newContext); - } else if(is_values<value_type>::value || is_select<value_type>::value) { + } else if(is_values_v<value_type> || is_select_v<value_type>) { auto newContext = context; newContext.use_parentheses = false; ss << serialize(value, newContext); @@ -14390,256 +16238,238 @@ namespace sqlite_orm { }; template<class T, class... Ids> - struct statement_serializator<remove_t<T, Ids...>, void> { + struct statement_serializer<remove_t<T, Ids...>, void> { using statement_type = remove_t<T, Ids...>; - template<class C> - std::string operator()(const statement_type&, const C& context) const { - auto& tImpl = context.impl.template get_impl<T>(); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + auto& table = pick_table<T>(context.db_objects); std::stringstream ss; - ss << "DELETE FROM '" << tImpl.table.name << "' "; - ss << "WHERE "; - auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << "\"" << primaryKeyColumnNames[i] << "\"" - << " = ? "; - if(i < primaryKeyColumnNames.size() - 1) { - ss << "AND "; + ss << "DELETE FROM " << streaming_identifier(table.name) << " " + << "WHERE "; + std::vector<std::string> idsStrings; + idsStrings.reserve(std::tuple_size<typename statement_type::ids_type>::value); + iterate_tuple(statement.ids, [&idsStrings, &context](auto& idValue) { + idsStrings.push_back(serialize(idValue, context)); + }); + table.for_each_primary_key_column([&table, &ss, &idsStrings, index = 0](auto& memberPointer) mutable { + auto* columnName = table.find_column_name(memberPointer); + if(!columnName) { + throw std::system_error{orm_error_code::column_not_found}; } - } + + constexpr std::array<const char*, 2> sep = {" AND ", ""}; + ss << sep[index == 0] << streaming_identifier(*columnName) << " = " << idsStrings[index]; + ++index; + }); return ss.str(); } }; - template<class T, class C> - std::string serialize_replace_range_impl(const T& rep, const C& context, const int valuesCount) { - using expression_type = typename std::decay<decltype(rep)>::type; - using object_type = typename expression_object_type<expression_type>::type; - auto& tImpl = context.impl.template get_impl<object_type>(); - std::stringstream ss; - ss << "REPLACE INTO '" << tImpl.table.name << "' ("; - - auto columnIndex = 0; - auto columnsCount = tImpl.table.count_columns_amount(); - tImpl.table.for_each_column([&ss, &columnIndex, columnsCount](auto& column) { - ss << " \"" << column.name << "\""; - if(columnIndex < columnsCount - 1) { - ss << ","; - } else { - ss << ")"; - } - ++columnIndex; - }); - ss << " VALUES "; - auto valuesString = [columnsCount] { - std::stringstream ss_; - ss_ << "("; - for(auto i = 0; i < columnsCount; ++i) { - ss_ << "?"; - if(i < columnsCount - 1) { - ss_ << ", "; - } else { - ss_ << ")"; - } - } - return ss_.str(); - }(); - for(auto i = 0; i < valuesCount; ++i) { - ss << valuesString; - if(i < valuesCount - 1) { - ss << ","; - } - ss << " "; - } - return ss.str(); - } - template<class It, class L, class O> - struct statement_serializator<replace_range_t<It, L, O>, void> { + struct statement_serializer<replace_range_t<It, L, O>, void> { using statement_type = replace_range_t<It, L, O>; - template<class C> - std::string operator()(const statement_type& rep, const C& context) const { - auto valuesCount = static_cast<int>(std::distance(rep.range.first, rep.range.second)); - return serialize_replace_range_impl(rep, context, valuesCount); + template<class Ctx> + std::string operator()(const statement_type& rep, const Ctx& context) const { + using expression_type = std::decay_t<decltype(rep)>; + using object_type = typename expression_object_type<expression_type>::type; + auto& table = pick_table<object_type>(context.db_objects); + + std::stringstream ss; + ss << "REPLACE INTO " << streaming_identifier(table.name) << " (" + << streaming_non_generated_column_names(table) << ")"; + const auto valuesCount = std::distance(rep.range.first, rep.range.second); + const auto columnsCount = table.non_generated_columns_count(); + ss << " VALUES " << streaming_values_placeholders(columnsCount, valuesCount); + return ss.str(); } }; template<class It, class L, class O> - struct statement_serializator<insert_range_t<It, L, O>, void> { + struct statement_serializer<insert_range_t<It, L, O>, void> { using statement_type = insert_range_t<It, L, O>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { - const auto valuesCount = static_cast<int>(std::distance(statement.range.first, statement.range.second)); - return serialize_insert_range_impl(statement, context, valuesCount); + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + using object_type = typename expression_object_type<statement_type>::type; + auto& table = pick_table<object_type>(context.db_objects); + using is_without_rowid = typename std::decay_t<decltype(table)>::is_without_rowid; + + std::vector<std::reference_wrapper<const std::string>> columnNames; + table.template for_each_column_excluding< + mpl::conjunction<mpl::not_<mpl::always<is_without_rowid>>, + mpl::disjunction_fn<is_primary_key, is_generated_always>>>( + [&table, &columnNames](auto& column) { + if(table.exists_in_composite_primary_key(column)) { + return; + } + + columnNames.push_back(cref(column.name)); + }); + const size_t valuesCount = std::distance(statement.range.first, statement.range.second); + const size_t columnNamesCount = columnNames.size(); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(table.name) << " "; + if(columnNamesCount) { + ss << "(" << streaming_identifiers(columnNames) << ")"; + } else { + ss << "DEFAULT"; + } + ss << " VALUES "; + if(columnNamesCount) { + ss << streaming_values_placeholders(columnNamesCount, valuesCount); + } else if(valuesCount != 1) { + throw std::system_error{orm_error_code::cannot_use_default_value}; + } + return ss.str(); } }; - template<class T, class C> - std::string serialize_get_all_impl(const T& get, const C& context) { - using primary_type = typename T::type; + template<class T, class Ctx> + std::string serialize_get_all_impl(const T& get, const Ctx& context) { + using primary_type = type_t<T>; table_name_collector collector; - collector.table_names.insert( - std::make_pair(context.impl.find_table_name(typeid(primary_type)), std::string{})); - iterate_ast(get.conditions, collector); + collector.table_names.emplace(lookup_table_name<primary_type>(context.db_objects), ""); + // note: not collecting table names from get.conditions; + + auto& table = pick_table<primary_type>(context.db_objects); std::stringstream ss; - ss << "SELECT"; - auto& tImpl = context.impl.template get_impl<primary_type>(); - auto columnIndex = 0; - auto columnsCount = tImpl.table.count_columns_amount(); - tImpl.table.for_each_column([&ss, &columnIndex, columnsCount, &tImpl](auto& column) { - ss << " \"" << tImpl.table.name << "\"." - << "\"" << column.name << "\""; - if(columnIndex < columnsCount - 1) { - ss << ","; - } - ++columnIndex; - }); - ss << " FROM "; - std::vector<std::pair<std::string, std::string>> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto& tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "' "; - if(!tableNamePair.second.empty()) { - ss << tableNamePair.second << " "; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ","; - } - ss << " "; + ss << "SELECT " << streaming_table_column_names(table, true); + if(!collector.table_names.empty()) { + ss << " FROM " << streaming_identifiers(collector.table_names); } - iterate_tuple(get.conditions, [&context, &ss](auto& v) { - ss << serialize(v, context); - }); + ss << streaming_conditions_tuple(get.conditions, context); return ss.str(); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class R, class... Args> - struct statement_serializator<get_all_optional_t<T, R, Args...>, void> { + struct statement_serializer<get_all_optional_t<T, R, Args...>, void> { using statement_type = get_all_optional_t<T, R, Args...>; - template<class C> - std::string operator()(const statement_type& get, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_all_impl(get, context); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class R, class... Args> - struct statement_serializator<get_all_pointer_t<T, R, Args...>, void> { + struct statement_serializer<get_all_pointer_t<T, R, Args...>, void> { using statement_type = get_all_pointer_t<T, R, Args...>; - template<class C> - std::string operator()(const statement_type& get, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_all_impl(get, context); } }; template<class T, class R, class... Args> - struct statement_serializator<get_all_t<T, R, Args...>, void> { + struct statement_serializer<get_all_t<T, R, Args...>, void> { using statement_type = get_all_t<T, R, Args...>; - template<class C> - std::string operator()(const statement_type& get, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_all_impl(get, context); } }; - template<class T, class C> - std::string serialize_get_impl(const T&, const C& context) { - using primary_type = typename T::type; - auto& tImpl = context.impl.template get_impl<primary_type>(); + template<class T, class Ctx> + std::string serialize_get_impl(const T&, const Ctx& context) { + using primary_type = type_t<T>; + auto& table = pick_table<primary_type>(context.db_objects); std::stringstream ss; - ss << "SELECT"; - auto columnIndex = 0; - auto columnsCount = tImpl.table.count_columns_amount(); - tImpl.table.for_each_column([&ss, &columnIndex, columnsCount](auto& column) { - ss << " \"" << column.name << "\""; - if(columnIndex < columnsCount - 1) { - ss << ", "; - } - ++columnIndex; - }); - ss << " FROM '" << tImpl.table.name << "' WHERE "; - auto primaryKeyColumnNames = tImpl.table.primary_key_column_names(); - if(!primaryKeyColumnNames.empty()) { - for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { - ss << " \"" << primaryKeyColumnNames[i] << "\"" - << " = ?"; - if(i < primaryKeyColumnNames.size() - 1) { - ss << " AND"; - } + ss << "SELECT " << streaming_table_column_names(table, false) << " FROM " + << streaming_identifier(table.name) << " WHERE "; + + auto primaryKeyColumnNames = table.primary_key_column_names(); + if(primaryKeyColumnNames.empty()) { + throw std::system_error{orm_error_code::table_has_no_primary_key_column}; + } + + for(size_t i = 0; i < primaryKeyColumnNames.size(); ++i) { + if(i > 0) { + ss << " AND "; } - return ss.str(); - } else { - throw std::system_error(std::make_error_code(orm_error_code::table_has_no_primary_key_column)); + ss << streaming_identifier(primaryKeyColumnNames[i]) << " = ?"; } + return ss.str(); } template<class T, class... Ids> - struct statement_serializator<get_t<T, Ids...>, void> { + struct statement_serializer<get_t<T, Ids...>, void> { using statement_type = get_t<T, Ids...>; - template<class C> - std::string operator()(const statement_type& get, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_impl(get, context); } }; template<class T, class... Ids> - struct statement_serializator<get_pointer_t<T, Ids...>, void> { + struct statement_serializer<get_pointer_t<T, Ids...>, void> { using statement_type = get_pointer_t<T, Ids...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { return serialize_get_impl(statement, context); } }; template<> - struct statement_serializator<insert_constraint, void> { - using statement_type = insert_constraint; + struct statement_serializer<conflict_action, void> { + using statement_type = conflict_action; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx&) const { switch(statement) { - case insert_constraint::abort: - return "OR ABORT"; - case insert_constraint::fail: - return "OR FAIL"; - case insert_constraint::ignore: - return "OR IGNORE"; - case insert_constraint::replace: - return "OR REPLACE"; - case insert_constraint::rollback: - return "OR ROLLBACK"; + case conflict_action::replace: + return "REPLACE"; + case conflict_action::abort: + return "ABORT"; + case conflict_action::fail: + return "FAIL"; + case conflict_action::ignore: + return "IGNORE"; + case conflict_action::rollback: + return "ROLLBACK"; } + return {}; + } + }; + + template<> + struct statement_serializer<insert_constraint, void> { + using statement_type = insert_constraint; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + return "OR " + serialize(statement.action, context); } }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Ids> - struct statement_serializator<get_optional_t<T, Ids...>, void> { + struct statement_serializer<get_optional_t<T, Ids...>, void> { using statement_type = get_optional_t<T, Ids...>; - template<class C> - std::string operator()(const statement_type& get, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& get, const Ctx& context) const { return serialize_get_impl(get, context); } }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Args> - struct statement_serializator<select_t<T, Args...>, void> { + struct statement_serializer<select_t<T, Args...>, void> { using statement_type = select_t<T, Args...>; - template<class C> - std::string operator()(const statement_type& sel, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& sel, const Ctx& context) const { std::stringstream ss; - const auto isCompoundOperator = is_base_of_template<T, compound_operator>::value; + constexpr bool isCompoundOperator = is_base_of_template_v<T, compound_operator>; if(!isCompoundOperator) { if(!sel.highest_level && context.use_parentheses) { ss << "("; @@ -14649,49 +16479,29 @@ namespace sqlite_orm { if(get_distinct(sel.col)) { ss << static_cast<std::string>(distinct(0)) << " "; } - auto columnNames = get_column_names(sel.col, context); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ", "; - } - } - table_name_collector collector([&context](std::type_index ti) { - return context.impl.find_table_name(ti); + ss << streaming_serialized(get_column_names(sel.col, context)); + table_name_collector collector([&context](const std::type_index& ti) { + return find_table_name(context.db_objects, ti); }); - const auto explicitFromItemsCount = count_tuple<std::tuple<Args...>, is_from>::value; + constexpr bool explicitFromItemsCount = count_tuple<std::tuple<Args...>, is_from>::value; if(!explicitFromItemsCount) { iterate_ast(sel.col, collector); iterate_ast(sel.conditions, collector); join_iterator<Args...>()([&collector, &context](const auto& c) { - using original_join_type = typename std::decay<decltype(c)>::type::join_type::type; - using cross_join_type = typename internal::mapped_type_proxy<original_join_type>::type; - auto crossJoinedTableName = context.impl.find_table_name(typeid(cross_join_type)); + using original_join_type = typename std::decay_t<decltype(c)>::join_type::type; + using cross_join_type = mapped_type_proxy_t<original_join_type>; + auto crossJoinedTableName = lookup_table_name<cross_join_type>(context.db_objects); auto tableAliasString = alias_extractor<original_join_type>::get(); - std::pair<std::string, std::string> tableNameWithAlias(std::move(crossJoinedTableName), - std::move(tableAliasString)); + std::pair<std::string, std::string> tableNameWithAlias{std::move(crossJoinedTableName), + std::move(tableAliasString)}; collector.table_names.erase(tableNameWithAlias); }); if(!collector.table_names.empty() && !isCompoundOperator) { - ss << " FROM "; - std::vector<std::pair<std::string, std::string>> tableNames(collector.table_names.begin(), - collector.table_names.end()); - for(size_t i = 0; i < tableNames.size(); ++i) { - auto& tableNamePair = tableNames[i]; - ss << "'" << tableNamePair.first << "'"; - if(!tableNamePair.second.empty()) { - ss << ' ' << tableNamePair.second; - } - if(int(i) < int(tableNames.size()) - 1) { - ss << ", "; - } - } + ss << " FROM " << streaming_identifiers(collector.table_names); } } - iterate_tuple(sel.conditions, [&context, &ss](auto& v) { - ss << ' ' << serialize(v, context); - }); - if(!is_base_of_template<T, compound_operator>::value) { + ss << streaming_conditions_tuple(sel.conditions, context); + if(!is_base_of_template_v<T, compound_operator>) { if(!sel.highest_level && context.use_parentheses) { ss << ")"; } @@ -14701,11 +16511,11 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<indexed_column_t<T>, void> { + struct statement_serializer<indexed_column_t<T>, void> { using statement_type = indexed_column_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << serialize(statement.column_or_expression, context); if(!statement._collation_name.empty()) { @@ -14720,7 +16530,7 @@ namespace sqlite_orm { ss << " ASC"; break; default: - throw std::system_error(std::make_error_code(orm_error_code::incorrect_order)); + throw std::system_error{orm_error_code::incorrect_order}; } } return ss.str(); @@ -14728,165 +16538,313 @@ namespace sqlite_orm { }; template<class... Cols> - struct statement_serializator<index_t<Cols...>, void> { + struct statement_serializer<index_t<Cols...>, void> { using statement_type = index_t<Cols...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << "CREATE "; if(statement.unique) { ss << "UNIQUE "; } - using elements_type = typename std::decay<decltype(statement)>::type::elements_type; - using head_t = typename std::tuple_element<0, elements_type>::type::column_type; - using indexed_type = typename table_type<head_t>::type; - ss << "INDEX IF NOT EXISTS '" << statement.name << "' ON '" - << context.impl.find_table_name(typeid(indexed_type)) << "' ("; + using elements_type = typename std::decay_t<decltype(statement)>::elements_type; + using head_t = typename std::tuple_element_t<0, elements_type>::column_type; + using indexed_type = table_type_of_t<head_t>; + ss << "INDEX IF NOT EXISTS " << streaming_identifier(statement.name) << " ON " + << streaming_identifier(lookup_table_name<indexed_type>(context.db_objects)); std::vector<std::string> columnNames; - iterate_tuple(statement.elements, [&columnNames, &context](auto& v) { - auto columnName = serialize(v, context); - columnNames.push_back(move(columnName)); - }); - for(size_t i = 0; i < columnNames.size(); ++i) { - ss << columnNames[i]; - if(i < columnNames.size() - 1) { - ss << ", "; + std::string whereString; + iterate_tuple(statement.elements, [&columnNames, &context, &whereString](auto& value) { + using value_type = std::decay_t<decltype(value)>; + if(!is_where_v<value_type>) { + auto newContext = context; + newContext.use_parentheses = false; + auto whereString = serialize(value, newContext); + columnNames.push_back(move(whereString)); + } else { + auto columnName = serialize(value, context); + whereString = move(columnName); } + }); + ss << " (" << streaming_serialized(columnNames) << ")"; + if(!whereString.empty()) { + ss << ' ' << whereString; } - ss << ")"; return ss.str(); } }; template<class... Args> - struct statement_serializator<from_t<Args...>, void> { + struct statement_serializer<from_t<Args...>, void> { using statement_type = from_t<Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type&, const Ctx& context) const { using tuple = std::tuple<Args...>; std::stringstream ss; ss << "FROM "; - size_t index = 0; - iterate_tuple<tuple>([&context, &ss, &index](auto* itemPointer) { - using mapped_type = typename std::remove_pointer<decltype(itemPointer)>::type; - - auto aliasString = alias_extractor<mapped_type>::get(); - ss << "'" << context.impl.find_table_name(typeid(typename mapped_type_proxy<mapped_type>::type)) - << "'"; - if(aliasString.length()) { - ss << " '" << aliasString << "'"; - } - if(index < std::tuple_size<tuple>::value - 1) { - ss << ", "; + iterate_tuple<tuple>([&context, &ss, first = true](auto* item) mutable { + using from_type = std::remove_pointer_t<decltype(item)>; + + constexpr std::array<const char*, 2> sep = {", ", ""}; + ss << sep[std::exchange(first, false)] + << streaming_identifier(lookup_table_name<mapped_type_proxy_t<from_type>>(context.db_objects), + alias_extractor<from_type>::get()); + }); + return ss.str(); + } + }; + + template<class T> + struct statement_serializer<old_t<T>, void> { + using statement_type = old_t<T>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "OLD."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<class T> + struct statement_serializer<new_t<T>, void> { + using statement_type = new_t<T>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "NEW."; + auto newContext = context; + newContext.skip_table_name = true; + ss << serialize(statement.expression, newContext); + return ss.str(); + } + }; + + template<> + struct statement_serializer<raise_t, void> { + using statement_type = raise_t; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + switch(statement.type) { + case raise_t::type_t::ignore: + return "RAISE(IGNORE)"; + + case raise_t::type_t::rollback: + return "RAISE(ROLLBACK, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::abort: + return "RAISE(ABORT, " + serialize(statement.message, context) + ")"; + + case raise_t::type_t::fail: + return "RAISE(FAIL, " + serialize(statement.message, context) + ")"; + } + return {}; + } + }; + + template<> + struct statement_serializer<trigger_timing, void> { + using statement_type = trigger_timing; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_timing::trigger_before: + return "BEFORE"; + case trigger_timing::trigger_after: + return "AFTER"; + case trigger_timing::trigger_instead_of: + return "INSTEAD OF"; + } + return {}; + } + }; + + template<> + struct statement_serializer<trigger_type, void> { + using statement_type = trigger_type; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx&) const { + switch(statement) { + case trigger_type::trigger_delete: + return "DELETE"; + case trigger_type::trigger_insert: + return "INSERT"; + case trigger_type::trigger_update: + return "UPDATE"; + } + return {}; + } + }; + + template<> + struct statement_serializer<trigger_type_base_t, void> { + using statement_type = trigger_type_base_t; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " " << serialize(statement.type, context); + return ss.str(); + } + }; + + template<class... Cs> + struct statement_serializer<trigger_update_type_t<Cs...>, void> { + using statement_type = trigger_update_type_t<Cs...>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.timing, context) << " UPDATE OF " + << streaming_mapped_columns_expressions(statement.columns, context); + return ss.str(); + } + }; + + template<class T, class W, class Trigger> + struct statement_serializer<trigger_base_t<T, W, Trigger>, void> { + using statement_type = trigger_base_t<T, W, Trigger>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + + ss << serialize(statement.type_base, context); + ss << " ON " << streaming_identifier(lookup_table_name<T>(context.db_objects)); + if(statement.do_for_each_row) { + ss << " FOR EACH ROW"; + } + statement.container_when.apply([&ss, &context](auto& value) { + ss << " WHEN " << serialize(value, context); + }); + return ss.str(); + } + }; + + template<class... S> + struct statement_serializer<trigger_t<S...>, void> { + using statement_type = trigger_t<S...>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + ss << "CREATE "; + + ss << "TRIGGER IF NOT EXISTS " << streaming_identifier(statement.name) << " " + << serialize(statement.base, context); + ss << " BEGIN "; + iterate_tuple(statement.elements, [&ss, &context](auto& element) { + using element_type = std::decay_t<decltype(element)>; + if(is_select_v<element_type>) { + auto newContext = context; + newContext.use_parentheses = false; + ss << serialize(element, newContext); + } else { + ss << serialize(element, context); } - ++index; + ss << ";"; }); + ss << " END"; + return ss.str(); } }; template<class T> - struct statement_serializator<where_t<T>, void> { + struct statement_serializer<where_t<T>, void> { using statement_type = where_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; ss << statement.serialize() << " "; auto whereString = serialize(statement.expression, context); - ss << "( " << whereString << ") "; + ss << '(' << whereString << ')'; return ss.str(); } }; template<class O> - struct statement_serializator<order_by_t<O>, void> { + struct statement_serializer<order_by_t<O>, void> { using statement_type = order_by_t<O>; - template<class C> - std::string operator()(const statement_type& orderBy, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; ss << static_cast<std::string>(orderBy) << " "; - auto orderByString = serialize_order_by(orderBy, context); - ss << orderByString << " "; + ss << serialize_order_by(orderBy, context); return ss.str(); } }; template<class C> - struct statement_serializator<dynamic_order_by_t<C>, void> { + struct statement_serializer<dynamic_order_by_t<C>, void> { using statement_type = dynamic_order_by_t<C>; - template<class CC> - std::string operator()(const statement_type& orderBy, const CC& context) const { + template<class Ctx> + std::string operator()(const statement_type& orderBy, const Ctx& context) const { return serialize_order_by(orderBy, context); } }; template<class... Args> - struct statement_serializator<multi_order_by_t<Args...>, void> { + struct statement_serializer<multi_order_by_t<Args...>, void> { using statement_type = multi_order_by_t<Args...>; - template<class C> - std::string operator()(const statement_type& orderBy, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& orderBy, const Ctx& context) const { std::stringstream ss; - std::vector<std::string> expressions; - iterate_tuple(orderBy.args, [&expressions, &context](auto& v) { - auto expression = serialize_order_by(v, context); - expressions.push_back(move(expression)); - }); - ss << static_cast<std::string>(orderBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; + ss << static_cast<std::string>(orderBy) << " " << streaming_expressions_tuple(orderBy.args, context); return ss.str(); } }; template<class O> - struct statement_serializator<cross_join_t<O>, void> { + struct statement_serializer<cross_join_t<O>, void> { using statement_type = cross_join_t<O>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(c) << " "; - ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + ss << static_cast<std::string>(c) << " " + << streaming_identifier(lookup_table_name<O>(context.db_objects)); return ss.str(); } }; template<class T, class O> - struct statement_serializator<inner_join_t<T, O>, void> { + struct statement_serializer<inner_join_t<T, O>, void> { using statement_type = inner_join_t<T, O>; - template<class C> - std::string operator()(const statement_type& l, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(l) << " "; - auto aliasString = alias_extractor<T>::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast<std::string>(l) << " " + << streaming_identifier(lookup_table_name<mapped_type_proxy_t<T>>(context.db_objects), + alias_extractor<T>::get()) + << serialize(l.constraint, context); return ss.str(); } }; template<class T> - struct statement_serializator<on_t<T>, void> { + struct statement_serializer<on_t<T>, void> { using statement_type = on_t<T>; - template<class C> - std::string operator()(const statement_type& t, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& t, const Ctx& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; @@ -14896,109 +16854,102 @@ namespace sqlite_orm { }; template<class T, class O> - struct statement_serializator<join_t<T, O>, void> { + struct statement_serializer<join_t<T, O>, void> { using statement_type = join_t<T, O>; - template<class C> - std::string operator()(const statement_type& l, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(l) << " "; - auto aliasString = alias_extractor<T>::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast<std::string>(l) << " " + << streaming_identifier(lookup_table_name<mapped_type_proxy_t<T>>(context.db_objects), + alias_extractor<T>::get()) + << " " << serialize(l.constraint, context); return ss.str(); } }; template<class T, class O> - struct statement_serializator<left_join_t<T, O>, void> { + struct statement_serializer<left_join_t<T, O>, void> { using statement_type = left_join_t<T, O>; - template<class C> - std::string operator()(const statement_type& l, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(l) << " "; - auto aliasString = alias_extractor<T>::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast<std::string>(l) << " " + << streaming_identifier(lookup_table_name<mapped_type_proxy_t<T>>(context.db_objects), + alias_extractor<T>::get()) + << " " << serialize(l.constraint, context); return ss.str(); } }; template<class T, class O> - struct statement_serializator<left_outer_join_t<T, O>, void> { + struct statement_serializer<left_outer_join_t<T, O>, void> { using statement_type = left_outer_join_t<T, O>; - template<class C> - std::string operator()(const statement_type& l, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& l, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(l) << " "; - auto aliasString = alias_extractor<T>::get(); - ss << " '" << context.impl.find_table_name(typeid(typename mapped_type_proxy<T>::type)) << "' "; - if(aliasString.length()) { - ss << "'" << aliasString << "' "; - } - ss << serialize(l.constraint, context); + ss << static_cast<std::string>(l) << " " + << streaming_identifier(lookup_table_name<mapped_type_proxy_t<T>>(context.db_objects), + alias_extractor<T>::get()) + << " " << serialize(l.constraint, context); return ss.str(); } }; template<class O> - struct statement_serializator<natural_join_t<O>, void> { + struct statement_serializer<natural_join_t<O>, void> { using statement_type = natural_join_t<O>; - template<class C> - std::string operator()(const statement_type& c, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& c, const Ctx& context) const { std::stringstream ss; - ss << static_cast<std::string>(c) << " "; - ss << " '" << context.impl.find_table_name(typeid(O)) << "'"; + ss << static_cast<std::string>(c) << " " + << streaming_identifier(lookup_table_name<O>(context.db_objects)); + return ss.str(); + } + }; + + template<class T, class... Args> + struct statement_serializer<group_by_with_having<T, Args...>, void> { + using statement_type = group_by_with_having<T, Args...>; + + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { + std::stringstream ss; + auto newContext = context; + newContext.skip_table_name = false; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext) << " HAVING " + << serialize(statement.expression, context); return ss.str(); } }; template<class... Args> - struct statement_serializator<group_by_t<Args...>, void> { + struct statement_serializer<group_by_t<Args...>, void> { using statement_type = group_by_t<Args...>; - template<class C> - std::string operator()(const statement_type& groupBy, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - std::vector<std::string> expressions; auto newContext = context; newContext.skip_table_name = false; - iterate_tuple(groupBy.args, [&expressions, &newContext](auto& v) { - auto expression = serialize(v, newContext); - expressions.push_back(expression); - }); - ss << static_cast<std::string>(groupBy) << " "; - for(size_t i = 0; i < expressions.size(); ++i) { - ss << expressions[i]; - if(i < expressions.size() - 1) { - ss << ", "; - } - } - ss << " "; + ss << "GROUP BY " << streaming_expressions_tuple(statement.args, newContext); return ss.str(); } }; template<class T> - struct statement_serializator<having_t<T>, void> { + struct statement_serializer<having_t<T>, void> { using statement_type = having_t<T>; - template<class C> - std::string operator()(const statement_type& hav, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; auto newContext = context; newContext.skip_table_name = false; - ss << static_cast<std::string>(hav) << " "; - ss << serialize(hav.t, newContext) << " "; + ss << "HAVING " << serialize(statement.expression, newContext); return ss.str(); } }; @@ -15008,11 +16959,11 @@ namespace sqlite_orm { * OI - offset is implicit */ template<class T, bool HO, bool OI, class O> - struct statement_serializator<limit_t<T, HO, OI, O>, void> { + struct statement_serializer<limit_t<T, HO, OI, O>, void> { using statement_type = limit_t<T, HO, OI, O>; - template<class C> - std::string operator()(const statement_type& limt, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& limt, const Ctx& context) const { auto newContext = context; newContext.skip_table_name = false; std::stringstream ss; @@ -15038,73 +16989,50 @@ namespace sqlite_orm { }; template<> - struct statement_serializator<default_values_t, void> { + struct statement_serializer<default_values_t, void> { using statement_type = default_values_t; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type&, const Ctx&) const { return "DEFAULT VALUES"; } }; - template<class F, class O> - struct statement_serializator<using_t<F, O>, void> { - using statement_type = using_t<F, O>; + template<class T, class M> + struct statement_serializer<using_t<T, M>, void> { + using statement_type = using_t<T, M>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { auto newContext = context; newContext.skip_table_name = true; - return static_cast<std::string>(statement) + " (" + serialize(statement.column, newContext) + " )"; + return static_cast<std::string>(statement) + " (" + serialize(statement.column, newContext) + ")"; } }; template<class... Args> - struct statement_serializator<std::tuple<Args...>, void> { + struct statement_serializer<std::tuple<Args...>, void> { using statement_type = std::tuple<Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; - ss << '('; - auto index = 0; - using TupleSize = std::tuple_size<statement_type>; - iterate_tuple(statement, [&context, &index, &ss](auto& value) { - ss << serialize(value, context); - if(index < TupleSize::value - 1) { - ss << ", "; - } - ++index; - }); - ss << ')'; + ss << '(' << streaming_expressions_tuple(statement, context) << ')'; return ss.str(); } }; template<class... Args> - struct statement_serializator<values_t<Args...>, void> { + struct statement_serializer<values_t<Args...>, void> { using statement_type = values_t<Args...>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; } - ss << "VALUES "; - { - auto index = 0; - auto& tuple = statement.tuple; - using tuple_type = typename std::decay<decltype(tuple)>::type; - using TupleSize = std::tuple_size<tuple_type>; - iterate_tuple(tuple, [&context, &index, &ss](auto& value) { - ss << serialize(value, context); - if(index < TupleSize::value - 1) { - ss << ", "; - } - ++index; - }); - } + ss << "VALUES " << streaming_expressions_tuple(statement.tuple, context); if(context.use_parentheses) { ss << ')'; } @@ -15113,37 +17041,26 @@ namespace sqlite_orm { }; template<class T> - struct statement_serializator<dynamic_values_t<T>, void> { + struct statement_serializer<dynamic_values_t<T>, void> { using statement_type = dynamic_values_t<T>; - template<class C> - std::string operator()(const statement_type& statement, const C& context) const { + template<class Ctx> + std::string operator()(const statement_type& statement, const Ctx& context) const { std::stringstream ss; if(context.use_parentheses) { ss << '('; } - ss << "VALUES "; - { - auto vectorSize = statement.vector.size(); - for(decltype(vectorSize) index = 0; index < vectorSize; ++index) { - auto& value = statement.vector[index]; - ss << serialize(value, context); - if(index < vectorSize - 1) { - ss << ", "; - } - } - } + ss << "VALUES " << streaming_dynamic_expressions(statement.vector, context); if(context.use_parentheses) { ss << ')'; } return ss.str(); } }; - } } -// #include "table_name_collector.h" +// #include "triggers.h" // #include "object_from_column_builder.h" @@ -15151,134 +17068,168 @@ namespace sqlite_orm { // #include "column.h" +// #include "index.h" + +// #include "util.h" + +// #include "serializing_util.h" + namespace sqlite_orm { namespace internal { + template<class S, class E, class SFINAE = void> + SQLITE_ORM_INLINE_VAR constexpr bool is_preparable_v = false; + + template<class S, class E> + SQLITE_ORM_INLINE_VAR constexpr bool + is_preparable_v<S, E, polyfill::void_t<decltype(std::declval<S>().prepare(std::declval<E>()))>> = true; + /** * Storage class itself. Create an instanse to use it as an interfacto to sqlite db by calling `make_storage` * function. */ - template<class... Ts> + template<class... DBO> struct storage_t : storage_base { - using self = storage_t<Ts...>; - using impl_type = storage_impl<Ts...>; + using self = storage_t<DBO...>; + using db_objects_type = db_objects_tuple<DBO...>; /** * @param filename database filename. - * @param impl_ storage_impl head + * @param dbObjects db_objects_tuple */ - storage_t(const std::string& filename, impl_type impl_) : - storage_base{filename, foreign_keys_count(impl_)}, impl(std::move(impl_)) {} + storage_t(std::string filename, db_objects_type dbObjects) : + storage_base{move(filename), foreign_keys_count(dbObjects)}, db_objects{std::move(dbObjects)} {} - storage_t(const storage_t& other) : storage_base(other), impl(other.impl) {} - - protected: - impl_type impl; - - template<class T, class S, class... Args> - friend struct view_t; - - template<class S> - friend struct dynamic_order_by_t; + private: + db_objects_type db_objects; - template<class V> - friend struct iterator_t; + /** + * Obtain a storage_t's const db_objects_tuple. + * + * @note Historically, `serializer_context_builder` was declared friend, along with + * a few other library stock objects, in order to limit access to the db_objects_tuple. + * However, one could gain access to a storage_t's db_objects_tuple through + * `serializer_context_builder`, hence leading the whole friend declaration mambo-jumbo + * ad absurdum. + * Providing a free function is way better and cleaner. + * + * Hence, friend was replaced by `obtain_db_objects()` and `pick_const_impl()`. + */ + friend const db_objects_type& obtain_db_objects(const self& storage) noexcept { + return storage.db_objects; + } - template<class S> - friend struct serializator_context_builder; + template<class Table> + void create_table(sqlite3* db, const std::string& tableName, const Table& table) { + using table_type = std::decay_t<decltype(table)>; + using context_t = serializer_context<db_objects_type>; - template<class I> - void create_table(sqlite3* db, const std::string& tableName, const I& tableImpl) { - using table_type = typename std::decay<decltype(tableImpl.table)>::type; std::stringstream ss; - ss << "CREATE TABLE '" << tableName << "' ( "; - auto elementsCount = tableImpl.table.elements_count; - auto index = 0; - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - iterate_tuple(tableImpl.table.elements, [elementsCount, &index, &ss, &context](auto& element) { - ss << serialize(element, context); - if(index < elementsCount - 1) { - ss << ", "; - } - index++; - }); - ss << ")"; - if(table_type::is_without_rowid) { + context_t context{this->db_objects}; + ss << "CREATE TABLE " << streaming_identifier(tableName) << " ( " + << streaming_expressions_tuple(table.elements, context) << ")"; + if(table_type::is_without_rowid_v) { ss << " WITHOUT ROWID"; } + ss.flush(); + perform_void_exec(db, ss.str()); + } + + /** + * Copies sourceTableName to another table with name: destinationTableName + * Performs INSERT INTO %destinationTableName% () SELECT %table.column_names% FROM %sourceTableName% + */ + template<class Table> + void copy_table(sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector<const table_xinfo*>& columnsToIgnore) const; + +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + void drop_column(sqlite3* db, const std::string& tableName, const std::string& columnName) { + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " DROP COLUMN " + << streaming_identifier(columnName) << std::flush; perform_void_exec(db, ss.str()); } +#endif + + template<class Table> + void drop_create_with_loss(sqlite3* db, const Table& table) { + // eliminated all transaction handling + this->drop_table_internal(db, table.name); + this->create_table(db, table.name, table); + } - template<class I> - void backup_table(sqlite3* db, const I& tableImpl, const std::vector<table_info*>& columnsToIgnore) { + template<class Table> + void backup_table(sqlite3* db, const Table& table, const std::vector<const table_xinfo*>& columnsToIgnore) { // here we copy source table to another with a name with '_backup' suffix, but in case table with such // a name already exists we append suffix 1, then 2, etc until we find a free name.. - auto backupTableName = tableImpl.table.name + "_backup"; - if(tableImpl.table_exists(backupTableName, db)) { + auto backupTableName = table.name + "_backup"; + if(this->table_exists(db, backupTableName)) { int suffix = 1; do { - std::stringstream stream; - stream << suffix; - auto anotherBackupTableName = backupTableName + stream.str(); - if(!tableImpl.table_exists(anotherBackupTableName, db)) { - backupTableName = anotherBackupTableName; + std::stringstream ss; + ss << suffix << std::flush; + auto anotherBackupTableName = backupTableName + ss.str(); + if(!this->table_exists(db, anotherBackupTableName)) { + backupTableName = move(anotherBackupTableName); break; } ++suffix; } while(true); } + this->create_table(db, backupTableName, table); - this->create_table(db, backupTableName, tableImpl); - - tableImpl.copy_table(db, backupTableName, columnsToIgnore); + this->copy_table(db, table.name, backupTableName, table, columnsToIgnore); - this->drop_table_internal(tableImpl.table.name, db); + this->drop_table_internal(db, table.name); - tableImpl.rename_table(db, backupTableName, tableImpl.table.name); + this->rename_table(db, backupTableName, table.name); } template<class O> void assert_mapped_type() const { - using mapped_types_tuples = std::tuple<typename Ts::object_type...>; - static_assert(tuple_helper::has_type<O, mapped_types_tuples>::value, "type is not mapped to a storage"); + using mapped_types_tuple = std::tuple<typename DBO::object_type...>; + static_assert(mpl::invoke_t<check_if_tuple_has_type<O>, mapped_types_tuple>::value, + "type is not mapped to a storage"); } - template<class O> + template<class O, + class Table = storage_pick_table_t<O, db_objects_type>, + std::enable_if_t<Table::is_without_rowid_v, bool> = true> + void assert_insertable_type() const {} + + template<class O, + class Table = storage_pick_table_t<O, db_objects_type>, + std::enable_if_t<!Table::is_without_rowid_v, bool> = true> void assert_insertable_type() const { - auto& tImpl = this->get_impl<O>(); - using table_type = typename std::decay<decltype(tImpl.table)>::type; - using elements_type = typename std::decay<decltype(tImpl.table.elements)>::type; - - using is_without_rowid = std::integral_constant<bool, table_type::is_without_rowid>; - - static_if<is_without_rowid{}>( - [](auto&) {}, // all right. it's a "without_rowid" table - [](auto& tImpl) { // unfortunately, this static_assert's can't see an composite keys(( - std::ignore = tImpl; - static_assert( - count_tuple<elements_type, is_column_with_insertable_primary_key>::value <= 1, - "Attempting to execute 'insert' request into an noninsertable table was detected. " - "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " - "'insert', or you can use 'insert' with explicit column listing."); - static_assert( - count_tuple<elements_type, is_column_with_noninsertable_primary_key>::value == 0, - "Attempting to execute 'insert' request into an noninsertable table was detected. " - "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " - "of 'insert', or you can use 'insert' with explicit column listing."); - })(tImpl); + using elements_type = elements_type_t<Table>; + using pkcol_index_sequence = col_index_sequence_with<elements_type, is_primary_key>; + static_assert( + count_filtered_tuple<elements_type, is_primary_key_insertable, pkcol_index_sequence>::value <= 1, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " + "'insert', or you can use 'insert' with explicit column listing."); + static_assert(count_filtered_tuple<elements_type, + check_if_not<is_primary_key_insertable>::template fn, + pkcol_index_sequence>::value == 0, + "Attempting to execute 'insert' request into an noninsertable table was detected. " + "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " + "of 'insert', or you can use 'insert' with explicit column listing."); } template<class O> - auto& get_impl() const { - return this->impl.template get_impl<O>(); + auto& get_table() const { + return pick_table<O>(this->db_objects); } template<class O> - auto& get_impl() { - return this->impl.template get_impl<O>(); + auto& get_table() { + return pick_table<O>(this->db_objects); } public: @@ -15415,11 +17366,11 @@ namespace sqlite_orm { /** * Select * by id routine. - * throws std::system_error(orm_error_code::not_found, orm_error_category) if object not found with given + * throws std::system_error{orm_error_code::not_found} if object not found with given * id. throws std::system_error with orm_error_category in case of db error. O is an object type to be * extracted. Must be specified explicitly. * @return Object of type O where id is equal parameter passed or throws - * `std::system_error(orm_error_code::not_found, orm_error_category)` if there is no object with such id. + * `std::system_error{orm_error_code::not_found}` if there is no object with such id. */ template<class O, class... Ids> O get(Ids... ids) { @@ -15454,7 +17405,7 @@ namespace sqlite_orm { */ template<class O, class... Ids> std::shared_ptr<O> get_no_throw(Ids... ids) { - return std::shared_ptr<O>(get_pointer<O>(std::forward<Ids>(ids)...)); + return std::shared_ptr<O>(this->get_pointer<O>(std::forward<Ids>(ids)...)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED @@ -15474,7 +17425,7 @@ namespace sqlite_orm { * SELECT COUNT(*) https://www.sqlite.org/lang_aggfunc.html#count * @return Number of O object in table. */ - template<class O, class... Args, class R = typename mapped_type_proxy<O>::type> + template<class O, class... Args, class R = mapped_type_proxy_t<O>> int count(Args&&... args) { this->assert_mapped_type<R>(); auto rows = this->select(sqlite_orm::count<R>(), std::forward<Args>(args)...); @@ -15531,7 +17482,7 @@ namespace sqlite_orm { class O, class... Args, class Tuple = std::tuple<Args...>, - typename sfinae = typename std::enable_if<std::tuple_size<Tuple>::value >= 1>::type> + std::enable_if_t<std::tuple_size<Tuple>::value >= 1, bool> = true> std::string group_concat(F O::*m, Args&&... args) { return this->group_concat_internal(m, {}, std::forward<Args>(args)...); } @@ -15564,7 +17515,7 @@ namespace sqlite_orm { * @param m is a class member pointer (the same you passed into make_column). * @return std::unique_ptr with max value or null if sqlite engine returned null. */ - template<class F, class O, class... Args, class Ret = typename column_result_t<self, F O::*>::type> + template<class F, class O, class... Args, class Ret = column_result_of_t<db_objects_type, F O::*>> std::unique_ptr<Ret> max(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::max(m), std::forward<Args>(args)...); @@ -15580,7 +17531,7 @@ namespace sqlite_orm { * @param m is a class member pointer (the same you passed into make_column). * @return std::unique_ptr with min value or null if sqlite engine returned null. */ - template<class F, class O, class... Args, class Ret = typename column_result_t<self, F O::*>::type> + template<class F, class O, class... Args, class Ret = column_result_of_t<db_objects_type, F O::*>> std::unique_ptr<Ret> min(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); auto rows = this->select(sqlite_orm::min(m), std::forward<Args>(args)...); @@ -15596,7 +17547,7 @@ namespace sqlite_orm { * @param m is a class member pointer (the same you passed into make_column). * @return std::unique_ptr with sum value or null if sqlite engine returned null. */ - template<class F, class O, class... Args, class Ret = typename column_result_t<self, F O::*>::type> + template<class F, class O, class... Args, class Ret = column_result_of_t<db_objects_type, F O::*>> std::unique_ptr<Ret> sum(F O::*m, Args&&... args) { this->assert_mapped_type<O>(); std::vector<std::unique_ptr<double>> rows = @@ -15634,57 +17585,60 @@ namespace sqlite_orm { * For a single column use `auto rows = storage.select(&User::id, where(...)); * For multicolumns use `auto rows = storage.select(columns(&User::id, &User::name), where(...)); */ - template<class T, class... Args, class R = typename column_result_t<self, T>::type> + template<class T, class... Args, class R = column_result_of_t<db_objects_type, T>> std::vector<R> select(T m, Args... args) { - static_assert(!is_base_of_template<T, compound_operator>::value || + static_assert(!is_base_of_template_v<T, compound_operator> || std::tuple_size<std::tuple<Args...>>::value == 0, "Cannot use args with a compound operator"); auto statement = this->prepare(sqlite_orm::select(std::move(m), std::forward<Args>(args)...)); return this->execute(statement); } - template<class T> - typename std::enable_if<is_prepared_statement<T>::value, std::string>::type - dump(const T& preparedStatement) const { - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - return serialize(preparedStatement.t, context); + template<class T, satisfies<is_prepared_statement, T> = true> + std::string dump(const T& preparedStatement, bool parametrized = true) const { + return this->dump(preparedStatement.expression, parametrized); + } + + template<class E, + class Ex = polyfill::remove_cvref_t<E>, + std::enable_if_t<!is_prepared_statement_v<Ex> && !is_mapped_v<db_objects_type, Ex>, bool> = true> + std::string dump(E&& expression, bool parametrized = false) const { + static_assert(is_preparable_v<self, Ex>, "Expression must be a high-level statement"); + + decltype(auto) e2 = static_if<is_select_v<Ex>>( + [](auto expression) -> auto{ + expression.highest_level = true; + return expression; + }, + [](const auto& expression) -> decltype(auto) { + return (expression); + })(std::forward<E>(expression)); + using context_t = serializer_context<db_objects_type>; + context_t context{this->db_objects}; + context.replace_bindable_with_question = parametrized; + // just like prepare_impl() + context.skip_table_name = false; + return serialize(e2, context); } /** * Returns a string representation of object of a class mapped to the storage. * Type of string has json-like style. */ - template<class O> - typename std::enable_if<storage_traits::type_is_mapped<self, O>::value, std::string>::type - dump(const O& o) { - auto& tImpl = this->get_impl<O>(); + template<class O, satisfies<is_mapped, db_objects_type, O> = true> + std::string dump(const O& object) const { + auto& table = this->get_table<O>(); std::stringstream ss; ss << "{ "; - using pair = std::pair<std::string, std::string>; - std::vector<pair> pairs; - tImpl.table.for_each_column([&pairs, &o](auto& c) { - using column_type = typename std::decay<decltype(c)>::type; + table.for_each_column([&ss, &object, first = true](auto& column) mutable { + using column_type = std::decay_t<decltype(column)>; using field_type = typename column_type::field_type; - pair p{c.name, std::string()}; - if(c.member_pointer) { - p.second = field_printer<field_type>()(o.*c.member_pointer); - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; - p.second = field_printer<field_type>()(valueHolder.value); - } - pairs.push_back(move(p)); + constexpr std::array<const char*, 2> sep = {", ", ""}; + + ss << sep[std::exchange(first, false)] << column.name << " : '" + << field_printer<field_type>{}(polyfill::invoke(column.member_pointer, object)) << "'"; }); - for(size_t i = 0; i < pairs.size(); ++i) { - auto& p = pairs[i]; - ss << p.first << " : '" << p.second << "'"; - if(i < pairs.size() - 1) { - ss << ", "; - } else { - ss << " }"; - } - } + ss << " }"; return ss.str(); } @@ -15701,33 +17655,34 @@ namespace sqlite_orm { this->execute(statement); } - template<class It> - void replace_range(It from, It to) { - using O = typename std::iterator_traits<It>::value_type; + template<class It, class Projection = polyfill::identity> + void replace_range(It from, It to, Projection project = {}) { + using O = std::decay_t<decltype(polyfill::invoke(std::declval<Projection>(), *std::declval<It>()))>; this->assert_mapped_type<O>(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::replace_range(from, to)); + auto statement = + this->prepare(sqlite_orm::replace_range(std::move(from), std::move(to), std::move(project))); this->execute(statement); } - template<class T, class It, class L> - void replace_range(It from, It to, L transformer) { - this->assert_mapped_type<T>(); + template<class O, class It, class Projection = polyfill::identity> + void replace_range(It from, It to, Projection project = {}) { + this->assert_mapped_type<O>(); if(from == to) { return; } - auto statement = this->prepare(sqlite_orm::replace_range<T>(from, to, std::move(transformer))); + auto statement = + this->prepare(sqlite_orm::replace_range<O>(std::move(from), std::move(to), std::move(project))); this->execute(statement); } template<class O, class... Cols> int insert(const O& o, columns_t<Cols...> cols) { - constexpr const size_t colsCount = std::tuple_size<std::tuple<Cols...>>::value; - static_assert(colsCount > 0, "Use insert or replace with 1 argument instead"); + static_assert(cols.count > 0, "Use insert or replace with 1 argument instead"); this->assert_mapped_type<O>(); auto statement = this->prepare(sqlite_orm::insert(std::ref(o), std::move(cols))); return int(this->execute(statement)); @@ -15742,11 +17697,8 @@ namespace sqlite_orm { int insert(const O& o) { this->assert_mapped_type<O>(); this->assert_insertable_type<O>(); - - return call_insert_impl_and_catch_constraint_failed([this, &o]() { - auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); - return int(this->execute(statement)); - }); + auto statement = this->prepare(sqlite_orm::insert(std::ref(o))); + return int(this->execute(statement)); } /** @@ -15823,32 +17775,29 @@ namespace sqlite_orm { this->execute(statement); } - template<class It> - void insert_range(It from, It to) { - using O = typename std::iterator_traits<It>::value_type; + template<class It, class Projection = polyfill::identity> + void insert_range(It from, It to, Projection project = {}) { + using O = std::decay_t<decltype(polyfill::invoke(std::declval<Projection>(), *std::declval<It>()))>; this->assert_mapped_type<O>(); this->assert_insertable_type<O>(); if(from == to) { return; } - - call_insert_impl_and_catch_constraint_failed([this, from, to]() { - auto statement = this->prepare(sqlite_orm::insert_range(from, to)); - this->execute(statement); - }); + auto statement = + this->prepare(sqlite_orm::insert_range(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } - template<class T, class It, class L> - void insert_range(It from, It to, L transformer) { - this->assert_mapped_type<T>(); - this->assert_insertable_type<T>(); + template<class O, class It, class Projection = polyfill::identity> + void insert_range(It from, It to, Projection project = {}) { + this->assert_mapped_type<O>(); + this->assert_insertable_type<O>(); if(from == to) { return; } - call_insert_impl_and_catch_constraint_failed([this, from, to, transformer = std::move(transformer)]() { - auto statement = this->prepare(sqlite_orm::insert_range<T>(from, to, std::move(transformer))); - this->execute(statement); - }); + auto statement = + this->prepare(sqlite_orm::insert_range<O>(std::move(from), std::move(to), std::move(project))); + this->execute(statement); } /** @@ -15858,8 +17807,8 @@ namespace sqlite_orm { template<class O> void rename_table(std::string name) { this->assert_mapped_type<O>(); - auto& tImpl = this->get_impl<O>(); - tImpl.table.name = move(name); + auto& table = this->get_table<O>(); + table.name = move(name); } using storage_base::rename_table; @@ -15871,111 +17820,157 @@ namespace sqlite_orm { template<class O> const std::string& tablename() const { this->assert_mapped_type<O>(); - auto& tImpl = this->get_impl<O>(); - return tImpl.table.name; + auto& table = this->get_table<O>(); + return table.name; + } + + template<class F, class O> + [[deprecated("Use the more accurately named function `find_column_name()`")]] const std::string* + column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); } template<class F, class O> - const std::string* column_name(F O::*memberPointer) const { - return this->impl.column_name(memberPointer); + const std::string* find_column_name(F O::*memberPointer) const { + return internal::find_column_name(this->db_objects, memberPointer); } protected: - template<class... Tss, class... Cols> - sync_schema_result schema_status(const storage_impl<index_t<Cols...>, Tss...>&, sqlite3*, bool) { + template<class... Cols> + sync_schema_result schema_status(const index_t<Cols...>&, sqlite3*, bool, bool*) { return sync_schema_result::already_in_sync; } - template<class T, bool WithoutRowId, class... Cs, class... Tss> - sync_schema_result schema_status(const storage_impl<table_t<T, WithoutRowId, Cs...>, Tss...>& tImpl, + template<class T, bool WithoutRowId, class... Cs> + sync_schema_result schema_status(const table_t<T, WithoutRowId, Cs...>& table, sqlite3* db, - bool preserve) { - return tImpl.schema_status(db, preserve); - } - - template<class... Tss, class... Cols> - sync_schema_result sync_table(const storage_impl<index_t<Cols...>, Tss...>& tableImpl, sqlite3* db, bool) { - auto res = sync_schema_result::already_in_sync; - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; - auto query = serialize(tableImpl.table, context); - perform_void_exec(db, query); - return res; - } + bool preserve, + bool* attempt_to_preserve) { + if(attempt_to_preserve) { + *attempt_to_preserve = true; + } - template<class T, bool WithoutRowId, class... Args, class... Tss> - sync_schema_result sync_table(const storage_impl<table_t<T, WithoutRowId, Args...>, Tss...>& tImpl, - sqlite3* db, - bool preserve) { + auto dbTableInfo = this->pragma.table_xinfo(table.name); auto res = sync_schema_result::already_in_sync; - auto schema_stat = tImpl.schema_status(db, preserve); - if(schema_stat != decltype(schema_stat)::already_in_sync) { - if(schema_stat == decltype(schema_stat)::new_table_created) { - this->create_table(db, tImpl.table.name, tImpl); - res = decltype(res)::new_table_created; - } else { - if(schema_stat == sync_schema_result::old_columns_removed || - schema_stat == sync_schema_result::new_columns_added || - schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { - - // get table info provided in `make_table` call.. - auto storageTableInfo = tImpl.table.get_table_info(); - - // now get current table info from db using `PRAGMA table_info` query.. - auto dbTableInfo = tImpl.get_table_info(tImpl.table.name, db); + // first let's see if table with such name exists.. + auto gottaCreateTable = !this->table_exists(db, table.name); + if(!gottaCreateTable) { - // this vector will contain pointers to columns that gotta be added.. - std::vector<table_info*> columnsToAdd; + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); - tImpl.calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + // this vector will contain pointers to columns that gotta be added.. + std::vector<const table_xinfo*> columnsToAdd; - if(schema_stat == sync_schema_result::old_columns_removed) { + if(calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo)) { + gottaCreateTable = true; + } - // extra table columns than storage columns - this->backup_table(db, tImpl, {}); - res = decltype(res)::old_columns_removed; + if(!gottaCreateTable) { // if all storage columns are equal to actual db columns but there are + // excess columns at the db.. + if(!dbTableInfo.empty()) { + // extra table columns than storage columns + if(!preserve) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + res = sync_schema_result::old_columns_removed; +#else + gottaCreateTable = true; +#endif + } else { + res = sync_schema_result::old_columns_removed; } - - if(schema_stat == sync_schema_result::new_columns_added) { - for(auto columnPointer: columnsToAdd) { - tImpl.add_column(*columnPointer, db); + } + } + if(gottaCreateTable) { + res = sync_schema_result::dropped_and_recreated; + } else { + if(!columnsToAdd.empty()) { + // extra storage columns than table columns + for(const table_xinfo* colInfo: columnsToAdd) { + const basic_generated_always::storage_type* generatedStorageType = + table.find_column_generated_storage_type(colInfo->name); + if(generatedStorageType) { + if(*generatedStorageType == basic_generated_always::storage_type::stored) { + gottaCreateTable = true; + break; + } + // fallback cause VIRTUAL can be added + } else { + if(colInfo->notnull && colInfo->dflt_value.empty()) { + gottaCreateTable = true; + // no matter if preserve is true or false, there is no way to preserve data, so we wont try! + if(attempt_to_preserve) { + *attempt_to_preserve = false; + }; + break; + } } - res = decltype(res)::new_columns_added; } - - if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { - - // remove extra columns - this->backup_table(db, tImpl, columnsToAdd); - res = decltype(res)::new_columns_added_and_old_columns_removed; + if(!gottaCreateTable) { + if(res == sync_schema_result::old_columns_removed) { + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } else { + res = sync_schema_result::new_columns_added; + } + } else { + res = sync_schema_result::dropped_and_recreated; + } + } else { + if(res != sync_schema_result::old_columns_removed) { + res = sync_schema_result::already_in_sync; } - } else if(schema_stat == sync_schema_result::dropped_and_recreated) { - this->drop_table_internal(tImpl.table.name, db); - this->create_table(db, tImpl.table.name, tImpl); - res = decltype(res)::dropped_and_recreated; } } + } else { + res = sync_schema_result::new_table_created; } return res; } + template<class... Cols> + sync_schema_result sync_table(const index_t<Cols...>& index, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; + using context_t = serializer_context<db_objects_type>; + context_t context{this->db_objects}; + auto query = serialize(index, context); + perform_void_exec(db, query); + return res; + } + + template<class... Cols> + sync_schema_result sync_table(const trigger_t<Cols...>& trigger, sqlite3* db, bool) { + auto res = sync_schema_result::already_in_sync; // TODO Change accordingly + using context_t = serializer_context<db_objects_type>; + context_t context{this->db_objects}; + perform_void_exec(db, serialize(trigger, context)); + return res; + } + + template<class Table, satisfies<is_table, Table> = true> + sync_schema_result sync_table(const Table& table, sqlite3* db, bool preserve); + + template<class C> + void add_column(sqlite3* db, const std::string& tableName, const C& column) const { + using context_t = serializer_context<db_objects_type>; + + context_t context{this->db_objects}; + std::stringstream ss; + ss << "ALTER TABLE " << streaming_identifier(tableName) << " ADD COLUMN " << serialize(column, context) + << std::flush; + perform_void_exec(db, ss.str()); + } + template<typename S> prepared_statement_t<S> prepare_impl(S statement) { - auto con = this->get_connection(); - sqlite3_stmt* stmt; - auto db = con.get(); - using context_t = serializator_context<impl_type>; - context_t context{this->impl}; + using context_t = serializer_context<db_objects_type>; + context_t context{this->db_objects}; context.skip_table_name = false; context.replace_bindable_with_question = true; - auto query = serialize(statement, context); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - return prepared_statement_t<S>{std::forward<S>(statement), stmt, con}; - } else { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + + auto con = this->get_connection(); + sqlite3_stmt* stmt = prepare_stmt(con.get(), serialize(statement, context)); + return prepared_statement_t<S>{std::forward<S>(statement), stmt, con}; } public: @@ -15991,16 +17986,16 @@ namespace sqlite_orm { * * if there are columns in storage that do not exist in db they will be added using `ALTER TABLE * ... ADD COLUMN ...' command * * if there is any column existing in both db and storage but differs by any of - * properties/constraints (type, pk, notnull, dflt_value) table will be dropped and recreated Be aware that + * properties/constraints (pk, notnull, dflt_value) table will be dropped and recreated. Be aware that * `sync_schema` doesn't guarantee that data will not be dropped. It guarantees only that it will make db * schema the same as you specified in `make_storage` function call. A good point is that if you have no db * file at all it will be created and all tables also will be created with exact tables and columns you - * specified in `make_storage`, `make_table` and `make_column` call. The best practice is to call this + * specified in `make_storage`, `make_table` and `make_column` calls. The best practice is to call this * function right after storage creation. - * @param preserve affects on function behaviour in case it is needed to remove a column. If it is `false` - * so table will be dropped if there is column to remove, if `true` - table is being copied into another - * table, dropped and copied table is renamed with source table name. Warning: sync_schema doesn't check - * foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please + * @param preserve affects function's behaviour in case it is needed to remove a column. If it is `false` + * so table will be dropped if there is column to remove if SQLite version is < 3.35.0 and rmeove column if SQLite version >= 3.35.0, + * if `true` - table is being copied into another table, dropped and copied table is renamed with source table name. + * Warning: sync_schema doesn't check foreign keys cause it is unable to do so in sqlite3. If you know how to get foreign key info please * submit an issue https://github.com/fnc12/sqlite_orm/issues * @return std::map with std::string key equal table name and `sync_schema_result` as value. * `sync_schema_result` is a enum value that stores table state after syncing a schema. `sync_schema_result` @@ -16009,10 +18004,9 @@ namespace sqlite_orm { std::map<std::string, sync_schema_result> sync_schema(bool preserve = false) { auto con = this->get_connection(); std::map<std::string, sync_schema_result> result; - auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto& storageImpl) { - auto res = this->sync_table(storageImpl, db, preserve); - result.insert({storageImpl.table.name, res}); + iterate_tuple<true>(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->sync_table(schemaObject, db, preserve); + result.emplace(schemaObject.name, status); }); return result; } @@ -16025,23 +18019,14 @@ namespace sqlite_orm { std::map<std::string, sync_schema_result> sync_schema_simulate(bool preserve = false) { auto con = this->get_connection(); std::map<std::string, sync_schema_result> result; - auto db = con.get(); - this->impl.for_each([&result, db, preserve, this](auto& tableImpl) { - auto schemaStatus = this->schema_status(tableImpl, db, preserve); - result.insert({tableImpl.table.name, schemaStatus}); + iterate_tuple<true>(this->db_objects, [this, db = con.get(), preserve, &result](auto& schemaObject) { + sync_schema_result status = this->schema_status(schemaObject, db, preserve, nullptr); + result.emplace(schemaObject.name, status); }); return result; } - /** - * Checks whether table exists in db. Doesn't check storage itself - works only with actual database. - * Note: table can be not mapped to a storage - * @return true if table with a given name exists in db, false otherwise. - */ - bool table_exists(const std::string& tableName) { - auto con = this->get_connection(); - return this->impl.table_exists(tableName, con.get()); - } + using storage_base::table_exists; // now that it is in storage_base make it into overload set template<class T, class... Args> prepared_statement_t<select_t<T, Args...>> prepare(select_t<T, Args...> sel) { @@ -16110,23 +18095,25 @@ namespace sqlite_orm { } template<class T, class... Ids> - prepared_statement_t<remove_t<T, Ids...>> prepare(remove_t<T, Ids...> rem) { - return prepare_impl<remove_t<T, Ids...>>(std::move(rem)); + prepared_statement_t<remove_t<T, Ids...>> prepare(remove_t<T, Ids...> statement) { + using object_type = typename expression_object_type<decltype(statement)>::type; + this->assert_mapped_type<object_type>(); + return this->prepare_impl<remove_t<T, Ids...>>(std::move(statement)); } template<class T> - prepared_statement_t<insert_t<T>> prepare(insert_t<T> ins) { - using object_type = typename expression_object_type<decltype(ins)>::type; + prepared_statement_t<insert_t<T>> prepare(insert_t<T> statement) { + using object_type = typename expression_object_type<decltype(statement)>::type; this->assert_mapped_type<object_type>(); this->assert_insertable_type<object_type>(); - return prepare_impl<insert_t<T>>(std::move(ins)); + return this->prepare_impl<insert_t<T>>(std::move(statement)); } template<class T> prepared_statement_t<replace_t<T>> prepare(replace_t<T> rep) { using object_type = typename expression_object_type<decltype(rep)>::type; this->assert_mapped_type<object_type>(); - return prepare_impl<replace_t<T>>(std::move(rep)); + return this->prepare_impl<replace_t<T>>(std::move(rep)); } template<class It, class L, class O> @@ -16134,672 +18121,333 @@ namespace sqlite_orm { using object_type = typename expression_object_type<decltype(statement)>::type; this->assert_mapped_type<object_type>(); this->assert_insertable_type<object_type>(); - return prepare_impl<insert_range_t<It, L, O>>(std::move(statement)); + return this->prepare_impl<insert_range_t<It, L, O>>(std::move(statement)); } template<class It, class L, class O> - prepared_statement_t<replace_range_t<It, L, O>> prepare(replace_range_t<It, L, O> rep) { - return prepare_impl<replace_range_t<It, L, O>>(std::move(rep)); + prepared_statement_t<replace_range_t<It, L, O>> prepare(replace_range_t<It, L, O> statement) { + using object_type = typename expression_object_type<decltype(statement)>::type; + this->assert_mapped_type<object_type>(); + return this->prepare_impl<replace_range_t<It, L, O>>(std::move(statement)); } template<class T, class... Cols> prepared_statement_t<insert_explicit<T, Cols...>> prepare(insert_explicit<T, Cols...> ins) { using object_type = typename expression_object_type<decltype(ins)>::type; this->assert_mapped_type<object_type>(); - return prepare_impl<insert_explicit<T, Cols...>>(std::move(ins)); + return this->prepare_impl<insert_explicit<T, Cols...>>(std::move(ins)); } template<class... Args> void execute(const prepared_statement_t<replace_raw_t<Args...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - sqlite3_reset(stmt); - auto index = 1; - iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{statement.stmt}); + perform_step(stmt); } template<class... Args> void execute(const prepared_statement_t<insert_raw_t<Args...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - sqlite3_reset(stmt); - auto index = 1; - iterate_ast(statement.t.args, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.args, conditional_binder{stmt}); + perform_step(stmt); } template<class T, class... Cols> int64 execute(const prepared_statement_t<insert_explicit<T, Cols...>>& statement) { - using statement_type = typename std::decay<decltype(statement)>::type; + using statement_type = std::decay_t<decltype(statement)>; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto& tImpl = this->get_impl<object_type>(); - auto& o = statement.t.obj; - sqlite3_reset(stmt); - iterate_tuple(statement.t.columns.columns, [&o, &index, &stmt, &tImpl, db](auto& m) { - using column_type = typename std::decay<decltype(m)>::type; - using field_type = typename column_result_t<self, column_type>::type; - const field_type* value = tImpl.table.template get_object_field_pointer<field_type>(o, m); - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, *value)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); - return sqlite3_last_insert_rowid(db); + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + tuple_value_binder{stmt}( + statement.expression.columns.columns, + [&table = this->get_table<object_type>(), &object = statement.expression.obj](auto& memberPointer) { + return table.object_field_value(object, memberPointer); + }); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } template<class T, - typename std::enable_if<(is_replace_range<T>::value || is_replace<T>::value)>::type* = nullptr> + std::enable_if_t<polyfill::disjunction_v<is_replace<T>, is_replace_range<T>>, bool> = true> void execute(const prepared_statement_t<T>& statement) { - using statement_type = typename std::decay<decltype(statement)>::type; + using statement_type = std::decay_t<decltype(statement)>; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; - auto& tImpl = this->get_impl<object_type>(); - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - sqlite3_reset(stmt); - - auto processObject = [&index, &stmt, &tImpl, db](auto& o) { - tImpl.table.for_each_column([&](auto& c) { - using column_type = typename std::decay<decltype(c)>::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table<object_type>(), + bind_value = field_value_binder{stmt}](auto& object) mutable { + table.template for_each_column_excluding<is_generated_always>( + call_as_template_base<column_field>([&bind_value, &object](auto& column) { + bind_value(polyfill::invoke(column.member_pointer, object)); + })); }; - static_if<is_replace_range<T>{}>( - [&processObject](auto& statement) { - auto& transformer = statement.t.transformer; - std::for_each( /// - statement.t.range.first, - statement.t.range.second, - [&processObject, &transformer](auto& object) { - auto& realObject = transformer(object); - processObject(realObject); - }); + static_if<is_replace_range_v<T>>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif }, - [&processObject](auto& statement) { - auto& o = get_object(statement.t); + [&processObject](auto& expression) { + const object_type& o = get_object(expression); processObject(o); - })(statement); - perform_step(db, stmt); + })(statement.expression); + + perform_step(stmt); } - template<class T, - typename std::enable_if<(is_insert_range<T>::value || is_insert<T>::value)>::type* = nullptr> + template<class T, std::enable_if_t<polyfill::disjunction_v<is_insert<T>, is_insert_range<T>>, bool> = true> int64 execute(const prepared_statement_t<T>& statement) { - using statement_type = typename std::decay<decltype(statement)>::type; + using statement_type = std::decay_t<decltype(statement)>; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; - auto index = 1; - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto& tImpl = this->get_impl<object_type>(); - auto compositeKeyColumnNames = tImpl.table.composite_key_columns_names(); - sqlite3_reset(stmt); - auto processObject = [&index, &stmt, &tImpl, &compositeKeyColumnNames, db](auto& o) { - tImpl.table.for_each_column([&](auto& c) { - using table_type = typename std::decay<decltype(tImpl.table)>::type; - if(table_type::is_without_rowid || !c.template has<primary_key_t<>>()) { - auto it = std::find(compositeKeyColumnNames.begin(), compositeKeyColumnNames.end(), c.name); - if(it == compositeKeyColumnNames.end()) { - using column_type = typename std::decay<decltype(c)>::type; - using field_type = typename column_type::field_type; - if(c.member_pointer) { - if(SQLITE_OK != - statement_binder<field_type>().bind(stmt, index++, o.*c.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(c.getter))()}; - if(SQLITE_OK != - statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + auto processObject = [&table = this->get_table<object_type>(), + bind_value = field_value_binder{stmt}](auto& object) mutable { + using is_without_rowid = typename std::decay_t<decltype(table)>::is_without_rowid; + table.template for_each_column_excluding< + mpl::conjunction<mpl::not_<mpl::always<is_without_rowid>>, + mpl::disjunction_fn<is_primary_key, is_generated_always>>>( + call_as_template_base<column_field>([&table, &bind_value, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bind_value(polyfill::invoke(column.member_pointer, object)); } - } - }); + })); }; - static_if<is_insert_range<T>{}>( - [&processObject](auto& statement) { - auto& transformer = statement.t.transformer; - std::for_each( /// - statement.t.range.first, - statement.t.range.second, - [&processObject, &transformer](auto& object) { - auto& realObject = transformer(object); - processObject(realObject); - }); + static_if<is_insert_range_v<T>>( + [&processObject](auto& expression) { +#if __cpp_lib_ranges >= 201911L + std::ranges::for_each(expression.range.first, + expression.range.second, + std::ref(processObject), + std::ref(expression.transformer)); +#else + auto& transformer = expression.transformer; + std::for_each(expression.range.first, + expression.range.second, + [&processObject, &transformer](auto& item) { + const object_type& object = polyfill::invoke(transformer, item); + processObject(object); + }); +#endif }, - [&processObject](auto& statement) { - auto& o = get_object(statement.t); + [&processObject](auto& expression) { + const object_type& o = get_object(expression); processObject(o); - })(statement); + })(statement.expression); - perform_step(db, stmt); - return sqlite3_last_insert_rowid(db); + perform_step(stmt); + return sqlite3_last_insert_rowid(sqlite3_db_handle(stmt)); } template<class T, class... Ids> void execute(const prepared_statement_t<remove_t<T, Ids...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay<decltype(v)>::type; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + perform_step(stmt); } template<class T> void execute(const prepared_statement_t<update_t<T>>& statement) { - using statement_type = typename std::decay<decltype(statement)>::type; + using statement_type = std::decay_t<decltype(statement)>; using expression_type = typename statement_type::expression_type; using object_type = typename expression_object_type<expression_type>::type; - auto con = this->get_connection(); - auto db = con.get(); - auto& tImpl = this->get_impl<object_type>(); - auto stmt = statement.stmt; - auto index = 1; - auto& o = get_object(statement.t); - sqlite3_reset(stmt); - tImpl.table.for_each_column([&o, stmt, &index, db, &tImpl](auto& column) { - if(!column.template has<primary_key_t<>>() && - !tImpl.table.exists_in_composite_primary_key(column)) { - using column_type = typename std::decay<decltype(column)>::type; - using field_type = typename column_type::field_type; - if(column.member_pointer) { - auto bind_res = - statement_binder<field_type>().bind(stmt, index++, o.*column.member_pointer); - if(SQLITE_OK != bind_res) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(column.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } - }); - tImpl.table.for_each_column([&o, stmt, &index, db, &tImpl](auto& column) { - if(column.template has<primary_key_t<>>() || tImpl.table.exists_in_composite_primary_key(column)) { - using column_type = typename std::decay<decltype(column)>::type; - using field_type = typename column_type::field_type; - if(column.member_pointer) { - if(SQLITE_OK != - statement_binder<field_type>().bind(stmt, index++, o.*column.member_pointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((o).*(column.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + auto& table = this->get_table<object_type>(); + + field_value_binder bind_value{stmt}; + auto& object = get_object(statement.expression); + table.template for_each_column_excluding<mpl::disjunction_fn<is_primary_key, is_generated_always>>( + call_as_template_base<column_field>([&table, &bind_value, &object](auto& column) { + if(!table.exists_in_composite_primary_key(column)) { + bind_value(polyfill::invoke(column.member_pointer, object)); } + })); + table.for_each_column([&table, &bind_value, &object](auto& column) { + if(column.template is<is_primary_key>() || table.exists_in_composite_primary_key(column)) { + bind_value(polyfill::invoke(column.member_pointer, object)); } }); - perform_step(db, stmt); + perform_step(stmt); } template<class T, class... Ids> std::unique_ptr<T> execute(const prepared_statement_t<get_pointer_t<T, Ids...>>& statement) { - auto& tImpl = this->get_impl<T>(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay<decltype(v)>::type; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::unique_ptr<T> res; + perform_step(stmt, [&table = this->get_table<T>(), &res](sqlite3_stmt* stmt) { + res = std::make_unique<T>(); + object_from_column_builder<T> builder{*res, stmt}; + table.for_each_column(builder); }); - auto stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto res = std::make_unique<T>(); - object_from_column_builder<T> builder{*res, stmt}; - tImpl.table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - return {}; - } break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + return res; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Ids> std::optional<T> execute(const prepared_statement_t<get_optional_t<T, Ids...>>& statement) { - auto& tImpl = this->get_impl<T>(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay<decltype(v)>::type; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + + std::optional<T> res; + perform_step(stmt, [&table = this->get_table<T>(), &res](sqlite3_stmt* stmt) { + object_from_column_builder<T> builder{res.emplace(), stmt}; + table.for_each_column(builder); }); - auto stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto res = std::make_optional<T>(); - object_from_column_builder<T> builder{res.value(), stmt}; - tImpl.table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - return {}; - } break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } + return res; } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Ids> T execute(const prepared_statement_t<get_t<T, Ids...>>& statement) { - auto& tImpl = this->get_impl<T>(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.ids, [stmt, &index, db](auto& v) { - using field_type = typename std::decay<decltype(v)>::type; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, index++, v)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression.ids, conditional_binder{stmt}); + +#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED + std::optional<T> res; + perform_step(stmt, [&table = this->get_table<T>(), &res](sqlite3_stmt* stmt) { + object_from_column_builder<T> builder{res.emplace(), stmt}; + table.for_each_column(builder); }); - auto stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - T res; - object_from_column_builder<T> builder{res, stmt}; - tImpl.table.for_each_column(builder); - return res; - } break; - case SQLITE_DONE: { - throw std::system_error(std::make_error_code(sqlite_orm::orm_error_code::not_found)); - } break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + if(!res.has_value()) { + throw std::system_error{orm_error_code::not_found}; } + return move(res).value(); +#else + auto& table = this->get_table<T>(); + auto stepRes = sqlite3_step(stmt); + switch(stepRes) { + case SQLITE_ROW: { + T res; + object_from_column_builder<T> builder{res, stmt}; + table.for_each_column(builder); + return res; + } break; + case SQLITE_DONE: { + throw std::system_error{orm_error_code::not_found}; + } break; + default: { + throw_translated_sqlite_error(stmt); + } + } +#endif } template<class T, class... Args> void execute(const prepared_statement_t<remove_all_t<T, Args...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - perform_step(db, stmt); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + iterate_ast(statement.expression.conditions, conditional_binder{stmt}); + perform_step(stmt); } template<class... Args, class... Wargs> void execute(const prepared_statement_t<update_all_t<set_t<Args...>, Wargs...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_tuple(statement.t.set.assigns, [&index, stmt, db](auto& setArg) { - iterate_ast(setArg, [&index, stmt, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - }); - iterate_ast(statement.t.conditions, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + conditional_binder bind_node{stmt}; + iterate_tuple(statement.expression.set.assigns, [&bind_node](auto& setArg) { + iterate_ast(setArg, bind_node); }); - perform_step(db, stmt); + iterate_ast(statement.expression.conditions, bind_node); + perform_step(stmt); } - template<class T, class... Args, class R = typename column_result_t<self, T>::type> + template<class T, class... Args, class R = column_result_of_t<db_objects_type, T>> std::vector<R> execute(const prepared_statement_t<select_t<T, Args...>>& statement) { - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + std::vector<R> res; - auto tableInfoPointer = this->impl.template find_table<R>(); - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - using table_info_pointer_t = typename std::remove_pointer<decltype(tableInfoPointer)>::type; - using table_info_t = typename std::decay<table_info_pointer_t>::type; - row_extractor_builder<R, storage_traits::type_is_mapped<self, R>::value, table_info_t> - builder; - auto rowExtractor = builder(tableInfoPointer); - res.push_back(rowExtractor.extract(stmt, 0)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); + perform_steps(stmt, + [rowExtractor = make_row_extractor<R>(lookup_table<R>(this->db_objects)), + &res](sqlite3_stmt* stmt) { + res.push_back(rowExtractor.extract(stmt, 0)); + }); return res; } template<class T, class R, class... Args> R execute(const prepared_statement_t<get_all_t<T, R, Args...>>& statement) { - auto& tImpl = this->get_impl<T>(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + R res; - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - T obj; - object_from_column_builder<T> builder{obj, stmt}; - tImpl.table.for_each_column(builder); - res.push_back(std::move(obj)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); + perform_steps(stmt, [&table = this->get_table<T>(), &res](sqlite3_stmt* stmt) { + T obj; + object_from_column_builder<T> builder{obj, stmt}; + table.for_each_column(builder); + res.push_back(std::move(obj)); + }); return res; } template<class T, class R, class... Args> R execute(const prepared_statement_t<get_all_pointer_t<T, R, Args...>>& statement) { - auto& tImpl = this->get_impl<T>(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); + sqlite3_stmt* stmt = reset_stmt(statement.stmt); + + iterate_ast(statement.expression, conditional_binder{stmt}); + R res; - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto obj = std::make_unique<T>(); - object_from_column_builder<T> builder{*obj, stmt}; - tImpl.table.for_each_column(builder); - res.push_back(move(obj)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); + perform_steps(stmt, [&table = this->get_table<T>(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_unique<T>(); + object_from_column_builder<T> builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(move(obj)); + }); return res; } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class R, class... Args> R execute(const prepared_statement_t<get_all_optional_t<T, R, Args...>>& statement) { - auto& tImpl = this->get_impl<T>(); - auto con = this->get_connection(); - auto db = con.get(); - auto stmt = statement.stmt; - auto index = 1; - sqlite3_reset(stmt); - iterate_ast(statement.t, [stmt, &index, db](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - conditional_binder<node_type, is_bindable<node_type>> binder{stmt, index}; - if(SQLITE_OK != binder(node)) { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }); - R res; - int stepRes; - do { - stepRes = sqlite3_step(stmt); - switch(stepRes) { - case SQLITE_ROW: { - auto obj = std::make_optional<T>(); - object_from_column_builder<T> builder{*obj, stmt}; - tImpl.table.for_each_column(builder); - res.push_back(move(obj)); - } break; - case SQLITE_DONE: - break; - default: { - throw std::system_error(std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - } while(stepRes != SQLITE_DONE); - return res; - } -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED + sqlite3_stmt* stmt = reset_stmt(statement.stmt); - template<class O> - bool has_dependent_rows(const O& object) { - auto res = false; - this->impl.for_each([this, &object, &res](auto& storageImpl) { - if(res) { - return; - } - storageImpl.table.for_each_foreign_key([&storageImpl, this, &object, &res](auto& foreignKey) { - using ForeignKey = typename std::decay<decltype(foreignKey)>::type; - using TargetType = typename ForeignKey::target_type; - if(std::is_same<TargetType, O>::value) { - std::stringstream ss; - ss << "SELECT COUNT(*)"; - ss << " FROM " << storageImpl.table.name; - ss << " WHERE"; - auto columnIndex = 0; - iterate_tuple(foreignKey.columns, [&ss, &columnIndex, &storageImpl](auto& column) { - if(columnIndex > 0) { - ss << " AND"; - } - if(auto columnName = storageImpl.table.find_column_name(column)) { - ss << ' ' << *columnName << " = ?"; - } else { - throw std::system_error( - std::make_error_code(sqlite_orm::orm_error_code::column_not_found)); - } - ++columnIndex; - }); - auto query = ss.str(); - ss.flush(); - auto con = this->get_connection(); - sqlite3_stmt* stmt; - auto db = con.get(); - if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { - statement_finalizer finalizer(stmt); - columnIndex = 1; - iterate_tuple( - foreignKey.references, - [&columnIndex, stmt, &object, db](auto& memberPointer) { - using MemberPointer = typename std::decay<decltype(memberPointer)>::type; - using field_type = typename member_traits<MemberPointer>::field_type; - // if(column.member_pointer) { - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, - columnIndex++, - object.*memberPointer)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - /*} else { - using getter_type = typename column_type::getter_type; - field_value_holder<getter_type> valueHolder{((object).*(column.getter))()}; - if(SQLITE_OK != statement_binder<field_type>().bind(stmt, columnIndex++, valueHolder.value)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - }*/ - }); - if(SQLITE_ROW != sqlite3_step(stmt)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - auto countResult = sqlite3_column_int(stmt, 0); - res = countResult > 0; - if(SQLITE_DONE != sqlite3_step(stmt)) { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } else { - throw std::system_error( - std::error_code(sqlite3_errcode(db), get_sqlite_error_category()), - sqlite3_errmsg(db)); - } - } - }); + iterate_ast(statement.expression, conditional_binder{stmt}); + + R res; + perform_steps(stmt, [&table = this->get_table<T>(), &res](sqlite3_stmt* stmt) { + auto obj = std::make_optional<T>(); + object_from_column_builder<T> builder{*obj, stmt}; + table.for_each_column(builder); + res.push_back(move(obj)); }); return res; } +#endif // SQLITE_ORM_OPTIONAL_SUPPORTED }; // struct storage_t - - template<class T> - struct is_storage : std::false_type {}; - - template<class... Ts> - struct is_storage<storage_t<Ts...>> : std::true_type {}; } - template<class... Ts> - internal::storage_t<Ts...> make_storage(const std::string& filename, Ts... tables) { - return {filename, internal::storage_impl<Ts...>(std::forward<Ts>(tables)...)}; + /* + * Factory function for a storage, from a database file and a bunch of database object definitions. + */ + template<class... DBO> + internal::storage_t<DBO...> make_storage(std::string filename, DBO... dbObjects) { + return {move(filename), internal::db_objects_tuple<DBO...>{std::forward<DBO>(dbObjects)...}}; } /** @@ -16811,46 +18459,38 @@ namespace sqlite_orm { } #pragma once -#if defined(_MSC_VER) -#if defined(__RESTORE_MIN__) -__pragma(pop_macro("min")) -#undef __RESTORE_MIN__ -#endif -#if defined(__RESTORE_MAX__) - __pragma(pop_macro("max")) -#undef __RESTORE_MAX__ -#endif -#endif // defined(_MSC_VER) -#pragma once - #include <tuple> // std::tuple #include <utility> // std::pair #include <functional> // std::reference_wrapper -#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED -#include <optional> // std::optional -#endif // SQLITE_ORM_OPTIONAL_SUPPORTED +// #include "functional/cxx_optional.h" - // #include "conditions.h" +// #include "tuple_helper/tuple_filter.h" - // #include "operators.h" +// #include "conditions.h" - // #include "select_constraints.h" +// #include "operators.h" - // #include "prepared_statement.h" +// #include "select_constraints.h" - // #include "optional_container.h" +// #include "prepared_statement.h" - // #include "core_functions.h" +// #include "optional_container.h" - // #include "function.h" +// #include "core_functions.h" + +// #include "function.h" + +// #include "ast/excluded.h" + +// #include "ast/upsert_clause.h" - // #include "ast/excluded.h" +// #include "ast/where.h" - // #include "ast/upsert_clause.h" +// #include "ast/into.h" - // #include "ast/where.h" +// #include "ast/group_by.h" - namespace sqlite_orm { +namespace sqlite_orm { namespace internal { @@ -16859,50 +18499,68 @@ __pragma(pop_macro("min")) using type = std::tuple<T>; }; + template<class T> + using node_tuple_t = typename node_tuple<T>::type; + template<> struct node_tuple<void, void> { using type = std::tuple<>; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct node_tuple<as_optional_t<T>, void> { - using type = typename node_tuple<T>::type; - }; + struct node_tuple<as_optional_t<T>, void> : node_tuple<T> {}; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class T> - struct node_tuple<std::reference_wrapper<T>, void> { - using type = typename node_tuple<T>::type; + struct node_tuple<std::reference_wrapper<T>, void> : node_tuple<T> {}; + + template<class... Args> + struct node_tuple<group_by_t<Args...>, void> : node_tuple<std::tuple<Args...>> {}; + + template<class T, class... Args> + struct node_tuple<group_by_with_having<T, Args...>, void> { + using args_tuple = node_tuple_t<std::tuple<Args...>>; + using expression_tuple = node_tuple_t<T>; + using type = tuple_cat_t<args_tuple, expression_tuple>; }; template<class... TargetArgs, class... ActionsArgs> - struct node_tuple<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> { - using type = typename node_tuple<std::tuple<ActionsArgs...>>::type; - }; + struct node_tuple<upsert_clause<std::tuple<TargetArgs...>, std::tuple<ActionsArgs...>>, void> + : node_tuple<std::tuple<ActionsArgs...>> {}; template<class... Args> struct node_tuple<set_t<Args...>, void> { - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class T> - struct node_tuple<excluded_t<T>, void> { - using type = typename node_tuple<T>::type; - }; + struct node_tuple<excluded_t<T>, void> : node_tuple<T> {}; template<class C> - struct node_tuple<where_t<C>, void> { - using node_type = where_t<C>; - using type = typename node_tuple<C>::type; - }; + struct node_tuple<where_t<C>, void> : node_tuple<C> {}; + + /** + * Column alias + */ + template<class A> + struct node_tuple<alias_holder<A>, void> : node_tuple<void> {}; + + /** + * Literal + */ + template<class T> + struct node_tuple<literal_holder<T>, void> : node_tuple<void> {}; + + template<class E> + struct node_tuple<order_by_t<E>, void> : node_tuple<E> {}; template<class T> - struct node_tuple<T, typename std::enable_if<is_base_of_template<T, binary_condition>::value>::type> { + struct node_tuple<T, std::enable_if_t<is_base_of_template_v<T, binary_condition>>> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple<left_type>::type; - using right_node_tuple = typename node_tuple<right_type>::type; - using type = typename conc_tuple<left_node_tuple, right_node_tuple>::type; + using left_node_tuple = node_tuple_t<left_type>; + using right_node_tuple = node_tuple_t<right_type>; + using type = tuple_cat_t<left_node_tuple, right_node_tuple>; }; template<class L, class R, class... Ds> @@ -16910,276 +18568,217 @@ __pragma(pop_macro("min")) using node_type = binary_operator<L, R, Ds...>; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_node_tuple = typename node_tuple<left_type>::type; - using right_node_tuple = typename node_tuple<right_type>::type; - using type = typename conc_tuple<left_node_tuple, right_node_tuple>::type; + using left_node_tuple = node_tuple_t<left_type>; + using right_node_tuple = node_tuple_t<right_type>; + using type = tuple_cat_t<left_node_tuple, right_node_tuple>; }; template<class... Args> struct node_tuple<columns_t<Args...>, void> { - using node_type = columns_t<Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class L, class A> struct node_tuple<dynamic_in_t<L, A>, void> { - using node_type = dynamic_in_t<L, A>; - using left_tuple = typename node_tuple<L>::type; - using right_tuple = typename node_tuple<A>::type; - using type = typename conc_tuple<left_tuple, right_tuple>::type; + using left_tuple = node_tuple_t<L>; + using right_tuple = node_tuple_t<A>; + using type = tuple_cat_t<left_tuple, right_tuple>; }; template<class L, class... Args> struct node_tuple<in_t<L, Args...>, void> { - using node_type = in_t<L, Args...>; - using left_tuple = typename node_tuple<L>::type; - using right_tuple = typename conc_tuple<typename node_tuple<Args>::type...>::type; - using type = typename conc_tuple<left_tuple, right_tuple>::type; + using left_tuple = node_tuple_t<L>; + using right_tuple = tuple_cat_t<node_tuple_t<Args>...>; + using type = tuple_cat_t<left_tuple, right_tuple>; }; template<class T> - struct node_tuple<T, typename std::enable_if<is_base_of_template<T, compound_operator>::value>::type> { + struct node_tuple<T, std::enable_if_t<is_base_of_template_v<T, compound_operator>>> { using node_type = T; using left_type = typename node_type::left_type; using right_type = typename node_type::right_type; - using left_tuple = typename node_tuple<left_type>::type; - using right_tuple = typename node_tuple<right_type>::type; - using type = typename conc_tuple<left_tuple, right_tuple>::type; + using left_tuple = node_tuple_t<left_type>; + using right_tuple = node_tuple_t<right_type>; + using type = tuple_cat_t<left_tuple, right_tuple>; }; template<class T, class... Args> struct node_tuple<select_t<T, Args...>, void> { - using node_type = select_t<T, Args...>; - using columns_tuple = typename node_tuple<T>::type; - using args_tuple = typename conc_tuple<typename node_tuple<Args>::type...>::type; - using type = typename conc_tuple<columns_tuple, args_tuple>::type; + using columns_tuple = node_tuple_t<T>; + using args_tuple = tuple_cat_t<node_tuple_t<Args>...>; + using type = tuple_cat_t<columns_tuple, args_tuple>; }; template<class... Args> struct node_tuple<insert_raw_t<Args...>, void> { - using node_type = insert_raw_t<Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class... Args> struct node_tuple<replace_raw_t<Args...>, void> { - using node_type = replace_raw_t<Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class T> - struct node_tuple<into_t<T>, void> { - using node_type = into_t<T>; - using type = std::tuple<>; - }; + struct node_tuple<into_t<T>, void> : node_tuple<void> {}; template<class... Args> struct node_tuple<values_t<Args...>, void> { - using node_type = values_t<Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class... Args> struct node_tuple<std::tuple<Args...>, void> { - using node_type = std::tuple<Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class T, class R, class... Args> struct node_tuple<get_all_t<T, R, Args...>, void> { - using node_type = get_all_t<T, R, Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class T, class... Args> struct node_tuple<get_all_pointer_t<T, Args...>, void> { - using node_type = get_all_pointer_t<T, Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<class T, class... Args> struct node_tuple<get_all_optional_t<T, Args...>, void> { - using node_type = get_all_optional_t<T, Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<class... Args, class... Wargs> struct node_tuple<update_all_t<set_t<Args...>, Wargs...>, void> { - using node_type = update_all_t<set_t<Args...>, Wargs...>; - using set_tuple = typename conc_tuple<typename node_tuple<Args>::type...>::type; - using conditions_tuple = typename conc_tuple<typename node_tuple<Wargs>::type...>::type; - using type = typename conc_tuple<set_tuple, conditions_tuple>::type; + using set_tuple = tuple_cat_t<node_tuple_t<Args>...>; + using conditions_tuple = tuple_cat_t<node_tuple_t<Wargs>...>; + using type = tuple_cat_t<set_tuple, conditions_tuple>; }; template<class T, class... Args> struct node_tuple<remove_all_t<T, Args...>, void> { - using node_type = remove_all_t<T, Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class T> - struct node_tuple<having_t<T>, void> { - using node_type = having_t<T>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<having_t<T>, void> : node_tuple<T> {}; template<class T, class E> - struct node_tuple<cast_t<T, E>, void> { - using node_type = cast_t<T, E>; - using type = typename node_tuple<E>::type; - }; + struct node_tuple<cast_t<T, E>, void> : node_tuple<E> {}; template<class T> - struct node_tuple<exists_t<T>, void> { - using node_type = exists_t<T>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<exists_t<T>, void> : node_tuple<T> {}; template<class T> - struct node_tuple<optional_container<T>, void> { - using node_type = optional_container<T>; - using type = typename node_tuple<T>::type; - }; - - template<> - struct node_tuple<optional_container<void>, void> { - using node_type = optional_container<void>; - using type = std::tuple<>; - }; + struct node_tuple<optional_container<T>, void> : node_tuple<T> {}; template<class A, class T, class E> struct node_tuple<like_t<A, T, E>, void> { - using node_type = like_t<A, T, E>; - using arg_tuple = typename node_tuple<A>::type; - using pattern_tuple = typename node_tuple<T>::type; - using escape_tuple = typename node_tuple<E>::type; - using type = typename conc_tuple<arg_tuple, pattern_tuple, escape_tuple>::type; + using arg_tuple = node_tuple_t<A>; + using pattern_tuple = node_tuple_t<T>; + using escape_tuple = node_tuple_t<E>; + using type = tuple_cat_t<arg_tuple, pattern_tuple, escape_tuple>; }; template<class A, class T> struct node_tuple<glob_t<A, T>, void> { - using node_type = glob_t<A, T>; - using arg_tuple = typename node_tuple<A>::type; - using pattern_tuple = typename node_tuple<T>::type; - using type = typename conc_tuple<arg_tuple, pattern_tuple>::type; + using arg_tuple = node_tuple_t<A>; + using pattern_tuple = node_tuple_t<T>; + using type = tuple_cat_t<arg_tuple, pattern_tuple>; }; template<class A, class T> struct node_tuple<between_t<A, T>, void> { - using node_type = between_t<A, T>; - using expression_tuple = typename node_tuple<A>::type; - using lower_tuple = typename node_tuple<T>::type; - using upper_tuple = typename node_tuple<T>::type; - using type = typename conc_tuple<expression_tuple, lower_tuple, upper_tuple>::type; + using expression_tuple = node_tuple_t<A>; + using lower_tuple = node_tuple_t<T>; + using upper_tuple = node_tuple_t<T>; + using type = tuple_cat_t<expression_tuple, lower_tuple, upper_tuple>; }; template<class T> - struct node_tuple<named_collate<T>, void> { - using node_type = named_collate<T>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<named_collate<T>, void> : node_tuple<T> {}; template<class T> - struct node_tuple<is_null_t<T>, void> { - using node_type = is_null_t<T>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<is_null_t<T>, void> : node_tuple<T> {}; template<class T> - struct node_tuple<is_not_null_t<T>, void> { - using node_type = is_not_null_t<T>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<is_not_null_t<T>, void> : node_tuple<T> {}; template<class C> - struct node_tuple<negated_condition_t<C>, void> { - using node_type = negated_condition_t<C>; - using type = typename node_tuple<C>::type; - }; + struct node_tuple<negated_condition_t<C>, void> : node_tuple<C> {}; template<class R, class S, class... Args> struct node_tuple<built_in_function_t<R, S, Args...>, void> { - using node_type = built_in_function_t<R, S, Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; + }; + + template<class R, class S, class... Args> + struct node_tuple<built_in_aggregate_function_t<R, S, Args...>, void> { + using type = tuple_cat_t<node_tuple_t<Args>...>; + }; + + template<class F, class W> + struct node_tuple<filtered_aggregate_function<F, W>, void> { + using left_tuple = node_tuple_t<F>; + using right_tuple = node_tuple_t<W>; + using type = tuple_cat_t<left_tuple, right_tuple>; }; template<class F, class... Args> struct node_tuple<function_call<F, Args...>, void> { - using node_type = function_call<F, Args...>; - using type = typename conc_tuple<typename node_tuple<Args>::type...>::type; + using type = tuple_cat_t<node_tuple_t<Args>...>; }; template<class T, class O> - struct node_tuple<left_join_t<T, O>, void> { - using node_type = left_join_t<T, O>; - using type = typename node_tuple<O>::type; - }; + struct node_tuple<left_join_t<T, O>, void> : node_tuple<O> {}; template<class T> - struct node_tuple<on_t<T>, void> { - using node_type = on_t<T>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<on_t<T>, void> : node_tuple<T> {}; + + // note: not strictly necessary as there's no binding support for USING; + // we provide it nevertheless, in line with on_t. + template<class T, class M> + struct node_tuple<using_t<T, M>, void> : node_tuple<column_pointer<T, M>> {}; template<class T, class O> - struct node_tuple<join_t<T, O>, void> { - using node_type = join_t<T, O>; - using type = typename node_tuple<O>::type; - }; + struct node_tuple<join_t<T, O>, void> : node_tuple<O> {}; template<class T, class O> - struct node_tuple<left_outer_join_t<T, O>, void> { - using node_type = left_outer_join_t<T, O>; - using type = typename node_tuple<O>::type; - }; + struct node_tuple<left_outer_join_t<T, O>, void> : node_tuple<O> {}; template<class T, class O> - struct node_tuple<inner_join_t<T, O>, void> { - using node_type = inner_join_t<T, O>; - using type = typename node_tuple<O>::type; - }; + struct node_tuple<inner_join_t<T, O>, void> : node_tuple<O> {}; template<class R, class T, class E, class... Args> struct node_tuple<simple_case_t<R, T, E, Args...>, void> { - using node_type = simple_case_t<R, T, E, Args...>; - using case_tuple = typename node_tuple<T>::type; - using args_tuple = typename conc_tuple<typename node_tuple<Args>::type...>::type; - using else_tuple = typename node_tuple<E>::type; - using type = typename conc_tuple<case_tuple, args_tuple, else_tuple>::type; + using case_tuple = node_tuple_t<T>; + using args_tuple = tuple_cat_t<node_tuple_t<Args>...>; + using else_tuple = node_tuple_t<E>; + using type = tuple_cat_t<case_tuple, args_tuple, else_tuple>; }; template<class L, class R> struct node_tuple<std::pair<L, R>, void> { - using node_type = std::pair<L, R>; - using left_tuple = typename node_tuple<L>::type; - using right_tuple = typename node_tuple<R>::type; - using type = typename conc_tuple<left_tuple, right_tuple>::type; + using left_tuple = node_tuple_t<L>; + using right_tuple = node_tuple_t<R>; + using type = tuple_cat_t<left_tuple, right_tuple>; }; template<class T, class E> - struct node_tuple<as_t<T, E>, void> { - using node_type = as_t<T, E>; - using type = typename node_tuple<E>::type; - }; + struct node_tuple<as_t<T, E>, void> : node_tuple<E> {}; template<class T> - struct node_tuple<limit_t<T, false, false, void>, void> { - using node_type = limit_t<T, false, false, void>; - using type = typename node_tuple<T>::type; - }; + struct node_tuple<limit_t<T, false, false, void>, void> : node_tuple<T> {}; template<class T, class O> struct node_tuple<limit_t<T, true, false, O>, void> { - using node_type = limit_t<T, true, false, O>; - using type = typename conc_tuple<typename node_tuple<T>::type, typename node_tuple<O>::type>::type; + using type = tuple_cat_t<node_tuple_t<T>, node_tuple_t<O>>; }; template<class T, class O> struct node_tuple<limit_t<T, true, true, O>, void> { - using node_type = limit_t<T, true, true, O>; - using type = typename conc_tuple<typename node_tuple<O>::type, typename node_tuple<T>::type>::type; + using type = tuple_cat_t<node_tuple_t<O>, node_tuple_t<T>>; }; } } @@ -17187,144 +18786,146 @@ __pragma(pop_macro("min")) #include <type_traits> // std::is_same, std::decay, std::remove_reference +// #include "functional/static_magic.h" + // #include "prepared_statement.h" // #include "ast_iterator.h" -// #include "static_magic.h" - // #include "expression_object_type.h" namespace sqlite_orm { template<int N, class It, class L, class O> auto& get(internal::prepared_statement_t<internal::insert_range_t<It, L, O>>& statement) { - return std::get<N>(statement.t.range); + return std::get<N>(statement.expression.range); } template<int N, class It, class L, class O> const auto& get(const internal::prepared_statement_t<internal::insert_range_t<It, L, O>>& statement) { - return std::get<N>(statement.t.range); + return std::get<N>(statement.expression.range); } template<int N, class It, class L, class O> auto& get(internal::prepared_statement_t<internal::replace_range_t<It, L, O>>& statement) { - return std::get<N>(statement.t.range); + return std::get<N>(statement.expression.range); } template<int N, class It, class L, class O> const auto& get(const internal::prepared_statement_t<internal::replace_range_t<It, L, O>>& statement) { - return std::get<N>(statement.t.range); + return std::get<N>(statement.expression.range); } template<int N, class T, class... Ids> auto& get(internal::prepared_statement_t<internal::get_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } template<int N, class T, class... Ids> const auto& get(const internal::prepared_statement_t<internal::get_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } template<int N, class T, class... Ids> auto& get(internal::prepared_statement_t<internal::get_pointer_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } template<int N, class T, class... Ids> const auto& get(const internal::prepared_statement_t<internal::get_pointer_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template<int N, class T, class... Ids> auto& get(internal::prepared_statement_t<internal::get_optional_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } template<int N, class T, class... Ids> const auto& get(const internal::prepared_statement_t<internal::get_optional_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } #endif // SQLITE_ORM_OPTIONAL_SUPPORTED template<int N, class T, class... Ids> auto& get(internal::prepared_statement_t<internal::remove_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } template<int N, class T, class... Ids> const auto& get(const internal::prepared_statement_t<internal::remove_t<T, Ids...>>& statement) { - return internal::get_ref(std::get<N>(statement.t.ids)); + return internal::get_ref(std::get<N>(statement.expression.ids)); } template<int N, class T> auto& get(internal::prepared_statement_t<internal::update_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template<int N, class T> const auto& get(const internal::prepared_statement_t<internal::update_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for update statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template<int N, class T, class... Cols> auto& get(internal::prepared_statement_t<internal::insert_explicit<T, Cols...>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.obj); } template<int N, class T, class... Cols> const auto& get(const internal::prepared_statement_t<internal::insert_explicit<T, Cols...>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.obj); } template<int N, class T> auto& get(internal::prepared_statement_t<internal::replace_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template<int N, class T> const auto& get(const internal::prepared_statement_t<internal::replace_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for replace statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template<int N, class T> auto& get(internal::prepared_statement_t<internal::insert_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template<int N, class T> const auto& get(const internal::prepared_statement_t<internal::insert_t<T>>& statement) { static_assert(N == 0, "get<> works only with 0 argument for insert statement"); - return internal::get_ref(statement.t.obj); + return internal::get_ref(statement.expression.object); } template<int N, class T> const auto& get(const internal::prepared_statement_t<T>& statement) { - using statement_type = typename std::decay<decltype(statement)>::type; + using statement_type = std::decay_t<decltype(statement)>; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple<expression_type>::type; - using bind_tuple = typename internal::bindable_filter<node_tuple>::type; - using result_tupe = typename std::tuple_element<static_cast<size_t>(N), bind_tuple>::type; - const result_tupe* result = nullptr; - auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - if(internal::is_bindable<node_type>::value) { + using node_tuple = internal::node_tuple_t<expression_type>; + using bind_tuple = internal::bindable_filter_t<node_tuple>; + using result_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>; + const result_type* result = nullptr; + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t<decltype(node)>; + if(internal::is_bindable_v<node_type>) { ++index; } if(index == N) { - internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto& r, auto& n) { - r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n); - })(result, node); + internal::call_if_constexpr<std::is_same<result_type, node_type>::value>( + [](auto& r, auto& n) { + r = &n; + }, + result, + node); } }); return internal::get_ref(*result); @@ -17332,24 +18933,407 @@ namespace sqlite_orm { template<int N, class T> auto& get(internal::prepared_statement_t<T>& statement) { - using statement_type = typename std::decay<decltype(statement)>::type; + using statement_type = std::decay_t<decltype(statement)>; using expression_type = typename statement_type::expression_type; - using node_tuple = typename internal::node_tuple<expression_type>::type; - using bind_tuple = typename internal::bindable_filter<node_tuple>::type; - using result_tupe = typename std::tuple_element<static_cast<size_t>(N), bind_tuple>::type; - result_tupe* result = nullptr; - auto index = -1; - internal::iterate_ast(statement.t, [&result, &index](auto& node) { - using node_type = typename std::decay<decltype(node)>::type; - if(internal::is_bindable<node_type>::value) { + using node_tuple = internal::node_tuple_t<expression_type>; + using bind_tuple = internal::bindable_filter_t<node_tuple>; + using result_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>; + result_type* result = nullptr; + + internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable { + using node_type = std::decay_t<decltype(node)>; + if(internal::is_bindable_v<node_type>) { ++index; } if(index == N) { - internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto& r, auto& n) { - r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n); - })(result, node); + internal::call_if_constexpr<std::is_same<result_type, node_type>::value>( + [](auto& r, auto& n) { + r = const_cast<std::remove_reference_t<decltype(r)>>(&n); + }, + result, + node); } }); return internal::get_ref(*result); } } +#pragma once + +/* + * Note: This feature needs constexpr variables with external linkage. + * which can be achieved before C++17's inline variables, but differs from compiler to compiler. + * Hence we make it only available for compilers supporting inline variables. + */ + +#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED +#include <type_traits> // std::integral_constant +#include <utility> // std::move + +// #include "functional/cxx_universal.h" + +// #include "pointer_value.h" + +namespace sqlite_orm { + + inline constexpr const char carray_pvt_name[] = "carray"; + using carray_pvt = std::integral_constant<const char*, carray_pvt_name>; + + template<typename P> + using carray_pointer_arg = pointer_arg<P, carray_pvt>; + template<typename P, typename D> + using carray_pointer_binding = pointer_binding<P, carray_pvt, D>; + template<typename P> + using static_carray_pointer_binding = static_pointer_binding<P, carray_pvt>; + + /** + * Wrap a pointer of type 'carray' and its deleter function for binding it to a statement. + * + * Unless the deleter yields a nullptr 'xDestroy' function the ownership of the pointed-to-object + * is transferred to the pointer binding, which will delete it through + * the deleter when the statement finishes. + */ + template<class P, class D> + auto bindable_carray_pointer(P* p, D d) noexcept -> pointer_binding<P, carray_pvt, D> { + return bindable_pointer<carray_pvt>(p, std::move(d)); + } + + /** + * Wrap a pointer of type 'carray' for binding it to a statement. + * + * Note: 'Static' means that ownership of the pointed-to-object won't be transferred + * and sqlite assumes the object pointed to is valid throughout the lifetime of a statement. + */ + template<class P> + auto statically_bindable_carray_pointer(P* p) noexcept -> static_pointer_binding<P, carray_pvt> { + return statically_bindable_pointer<carray_pvt>(p); + } + + /** + * Generalized form of the 'remember' SQL function that is a pass-through for values + * (it returns its argument unchanged using move semantics) but also saves the + * value that is passed through into a bound variable. + */ + template<typename P> + struct note_value_fn { + P operator()(P&& value, carray_pointer_arg<P> pv) const { + if(P* observer = pv) { + *observer = value; + } + return std::move(value); + } + + static constexpr const char* name() { + return "note_value"; + } + }; + + /** + * remember(V, $PTR) extension function https://sqlite.org/src/file/ext/misc/remember.c + */ + struct remember_fn : note_value_fn<int64> { + static constexpr const char* name() { + return "remember"; + } + }; +} +#endif +#pragma once + +#include <string> // std::string + +// #include "column.h" + +// #include "table.h" + +namespace sqlite_orm { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + struct dbstat { + std::string name; + std::string path; + int pageno = 0; + std::string pagetype; + int ncell = 0; + int payload = 0; + int unused = 0; + int mx_payload = 0; + int pgoffset = 0; + int pgsize = 0; + }; + + inline auto make_dbstat_table() { + return make_table("dbstat", + make_column("name", &dbstat::name), + make_column("path", &dbstat::path), + make_column("pageno", &dbstat::pageno), + make_column("pagetype", &dbstat::pagetype), + make_column("ncell", &dbstat::ncell), + make_column("payload", &dbstat::payload), + make_column("unused", &dbstat::unused), + make_column("mx_payload", &dbstat::mx_payload), + make_column("pgoffset", &dbstat::pgoffset), + make_column("pgsize", &dbstat::pgsize)); + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB +} +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ +#pragma once + +// #include "implementations/column_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include <memory> // std::make_unique + +// #include "../functional/cxx_core_features.h" + +// #include "../functional/static_magic.h" + +// #include "../functional/index_sequence_util.h" + +// #include "../tuple_helper/tuple_filter.h" + +// #include "../tuple_helper/tuple_traits.h" + +// #include "../default_value_extractor.h" + +// #include "../column.h" + +namespace sqlite_orm { + namespace internal { + + template<class... Op> + std::unique_ptr<std::string> column_constraints<Op...>::default_value() const { + using default_op_index_sequence = + filter_tuple_sequence_t<constraints_type, check_if_is_template<default_t>::template fn>; + + std::unique_ptr<std::string> value; + call_if_constexpr<default_op_index_sequence::size()>( + [&value](auto& constraints, auto op_index_sequence) { + using default_op_index_sequence = decltype(op_index_sequence); + constexpr size_t opIndex = first_index_sequence_value(default_op_index_sequence{}); + value = std::make_unique<std::string>(serialize_default_value(get<opIndex>(constraints))); + }, + this->constraints, + default_op_index_sequence{}); + return value; + } + + } +} + +// #include "implementations/table_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * (e.g. column_t -> default_value_extractor -> serializer_context -> db_objects_tuple -> table_t -> column_t) + * this file is also used to provide definitions of interface methods 'hitting the database'. + */ + +#include <type_traits> // std::decay_t +#include <utility> // std::move +#include <algorithm> // std::find_if, std::ranges::find + +// #include "../type_printer.h" + +// #include "../column.h" + +// #include "../table.h" + +namespace sqlite_orm { + namespace internal { + + template<class T, bool WithoutRowId, class... Cs> + std::vector<table_xinfo> table_t<T, WithoutRowId, Cs...>::get_table_info() const { + std::vector<table_xinfo> res; + res.reserve(size_t(filter_tuple_sequence_t<elements_type, is_column>::size())); + this->for_each_column([&res](auto& column) { + using field_type = field_type_t<std::decay_t<decltype(column)>>; + std::string dft; + if(auto d = column.default_value()) { + dft = move(*d); + } + res.emplace_back(-1, + column.name, + type_printer<field_type>().print(), + column.is_not_null(), + dft, + column.template is<is_primary_key>(), + column.is_generated()); + }); + auto compositeKeyColumnNames = this->composite_key_columns_names(); + for(size_t i = 0; i < compositeKeyColumnNames.size(); ++i) { + auto& columnName = compositeKeyColumnNames[i]; +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(res, columnName, &table_xinfo::name); +#else + auto it = std::find_if(res.begin(), res.end(), [&columnName](const table_xinfo& ti) { + return ti.name == columnName; + }); +#endif + if(it != res.end()) { + it->pk = static_cast<int>(i + 1); + } + } + return res; + } + + } +} + +// #include "implementations/storage_definitions.h" +/** @file Mainly existing to disentangle implementation details from circular and cross dependencies + * this file is also used to separate implementation details from the main header file, + * e.g. usage of the dbstat table. + */ + +#include <type_traits> // std::is_same +#include <sstream> +#include <functional> // std::reference_wrapper, std::cref +#include <algorithm> // std::find_if, std::ranges::find + +// #include "../dbstat.h" + +// #include "../util.h" + +// #include "../serializing_util.h" + +// #include "../storage.h" + +namespace sqlite_orm { + namespace internal { + + template<class... DBO> + template<class Table, satisfies<is_table, Table>> + sync_schema_result storage_t<DBO...>::sync_table(const Table& table, sqlite3* db, bool preserve) { +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + if(std::is_same<Table, dbstat>::value) { + return sync_schema_result::already_in_sync; + } +#endif // SQLITE_ENABLE_DBSTAT_VTAB + auto res = sync_schema_result::already_in_sync; + bool attempt_to_preserve = true; + + auto schema_stat = this->schema_status(table, db, preserve, &attempt_to_preserve); + if(schema_stat != sync_schema_result::already_in_sync) { + if(schema_stat == sync_schema_result::new_table_created) { + this->create_table(db, table.name, table); + res = sync_schema_result::new_table_created; + } else { + if(schema_stat == sync_schema_result::old_columns_removed || + schema_stat == sync_schema_result::new_columns_added || + schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + // get table info provided in `make_table` call.. + auto storageTableInfo = table.get_table_info(); + + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + + // this vector will contain pointers to columns that gotta be added.. + std::vector<const table_xinfo*> columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + if(schema_stat == sync_schema_result::old_columns_removed) { +#if SQLITE_VERSION_NUMBER >= 3035000 // DROP COLUMN feature exists (v3.35.0) + for(auto& tableInfo: dbTableInfo) { + this->drop_column(db, table.name, tableInfo.name); + } + res = sync_schema_result::old_columns_removed; +#else + // extra table columns than storage columns + this->backup_table(db, table, {}); + res = sync_schema_result::old_columns_removed; +#endif + } + + if(schema_stat == sync_schema_result::new_columns_added) { + for(const table_xinfo* colInfo: columnsToAdd) { + table.for_each_column([this, colInfo, &tableName = table.name, db](auto& column) { + if(column.name != colInfo->name) { + return; + } + this->add_column(db, tableName, column); + }); + } + res = sync_schema_result::new_columns_added; + } + + if(schema_stat == sync_schema_result::new_columns_added_and_old_columns_removed) { + + auto storageTableInfo = table.get_table_info(); + this->add_generated_cols(columnsToAdd, storageTableInfo); + + // remove extra columns and generated columns + this->backup_table(db, table, columnsToAdd); + res = sync_schema_result::new_columns_added_and_old_columns_removed; + } + } else if(schema_stat == sync_schema_result::dropped_and_recreated) { + // now get current table info from db using `PRAGMA table_xinfo` query.. + auto dbTableInfo = this->pragma.table_xinfo(table.name); // should include generated columns + auto storageTableInfo = table.get_table_info(); + + // this vector will contain pointers to columns that gotta be added.. + std::vector<const table_xinfo*> columnsToAdd; + + this->calculate_remove_add_columns(columnsToAdd, storageTableInfo, dbTableInfo); + + this->add_generated_cols(columnsToAdd, storageTableInfo); + + if(preserve && attempt_to_preserve) { + this->backup_table(db, table, columnsToAdd); + } else { + this->drop_create_with_loss(db, table); + } + res = schema_stat; + } + } + } + return res; + } + + template<class... DBO> + template<class Table> + void storage_t<DBO...>::copy_table( + sqlite3* db, + const std::string& sourceTableName, + const std::string& destinationTableName, + const Table& table, + const std::vector<const table_xinfo*>& columnsToIgnore) const { // must ignore generated columns + std::vector<std::reference_wrapper<const std::string>> columnNames; + columnNames.reserve(table.count_columns_amount()); + table.for_each_column([&columnNames, &columnsToIgnore](const column_identifier& column) { + auto& columnName = column.name; +#if __cpp_lib_ranges >= 201911L + auto columnToIgnoreIt = std::ranges::find(columnsToIgnore, columnName, &table_xinfo::name); +#else + auto columnToIgnoreIt = std::find_if(columnsToIgnore.begin(), + columnsToIgnore.end(), + [&columnName](const table_xinfo* tableInfo) { + return columnName == tableInfo->name; + }); +#endif + if(columnToIgnoreIt == columnsToIgnore.end()) { + columnNames.push_back(cref(columnName)); + } + }); + + std::stringstream ss; + ss << "INSERT INTO " << streaming_identifier(destinationTableName) << " (" + << streaming_identifiers(columnNames) << ") " + << "SELECT " << streaming_identifiers(columnNames) << " FROM " << streaming_identifier(sourceTableName) + << std::flush; + perform_void_exec(db, ss.str()); + } + } +} + +#pragma once + +#if defined(_MSC_VER) +__pragma(pop_macro("max")) +__pragma(pop_macro("min")) +#endif // defined(_MSC_VER) diff --git a/public/steam/isteamapplist.h b/public/steam/isteamapplist.h index e83a52be..eb574354 100644 --- a/public/steam/isteamapplist.h +++ b/public/steam/isteamapplist.h @@ -47,7 +47,7 @@ STEAM_DEFINE_USER_INTERFACE_ACCESSOR( ISteamAppList *, SteamAppList, STEAMAPPLIS //---------------------------------------------------------------------------------
-// Purpose: Sent when a new app is installed
+// Purpose: Sent when a new app is installed (not downloaded yet)
//---------------------------------------------------------------------------------
STEAM_CALLBACK_BEGIN( SteamAppInstalled_t, k_iSteamAppListCallbacks + 1 )
STEAM_CALLBACK_MEMBER( 0, AppId_t, m_nAppID ) // ID of the app that installs
diff --git a/public/steam/isteamapps.h b/public/steam/isteamapps.h index 6cd3ddb1..908a7627 100644 --- a/public/steam/isteamapps.h +++ b/public/steam/isteamapps.h @@ -108,6 +108,9 @@ public: // check if game is a timed trial with limited playtime
virtual bool BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed ) = 0;
+
+ // set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions
+ virtual bool SetDlcContext( AppId_t nAppID ) = 0;
};
#define STEAMAPPS_INTERFACE_VERSION "STEAMAPPS_INTERFACE_VERSION008"
@@ -134,30 +137,6 @@ struct DlcInstalled_t };
-//-----------------------------------------------------------------------------
-// Purpose: possible results when registering an activation code
-//-----------------------------------------------------------------------------
-enum ERegisterActivationCodeResult
-{
- k_ERegisterActivationCodeResultOK = 0,
- k_ERegisterActivationCodeResultFail = 1,
- k_ERegisterActivationCodeResultAlreadyRegistered = 2,
- k_ERegisterActivationCodeResultTimeout = 3,
- k_ERegisterActivationCodeAlreadyOwned = 4,
-};
-
-
-//-----------------------------------------------------------------------------
-// Purpose: response to RegisterActivationCode()
-//-----------------------------------------------------------------------------
-struct RegisterActivationCodeResponse_t
-{
- enum { k_iCallback = k_iSteamAppsCallbacks + 8 };
- ERegisterActivationCodeResult m_eResult;
- uint32 m_unPackageRegistered; // package that was registered. Only set on success
-};
-
-
//---------------------------------------------------------------------------------
// Purpose: posted after the user gains executes a Steam URL with command line or query parameters
// such as steam://run/<appid>//-commandline/?param1=value1¶m2=value2¶m3=value3 etc
diff --git a/public/steam/isteamcontroller.h b/public/steam/isteamcontroller.h index ce9ff1e7..193a131a 100644 --- a/public/steam/isteamcontroller.h +++ b/public/steam/isteamcontroller.h @@ -21,9 +21,9 @@ #define STEAM_CONTROLLER_MAX_COUNT 16
-#define STEAM_CONTROLLER_MAX_ANALOG_ACTIONS 16
+#define STEAM_CONTROLLER_MAX_ANALOG_ACTIONS 24
-#define STEAM_CONTROLLER_MAX_DIGITAL_ACTIONS 128
+#define STEAM_CONTROLLER_MAX_DIGITAL_ACTIONS 256
#define STEAM_CONTROLLER_MAX_ORIGINS 8
@@ -448,6 +448,16 @@ enum EControllerActionOrigin k_EControllerActionOrigin_SteamDeck_Reserved19,
k_EControllerActionOrigin_SteamDeck_Reserved20,
+ k_EControllerActionOrigin_Switch_JoyConButton_N, // With a Horizontal JoyCon this will be Y or what would be Dpad Right when vertical
+ k_EControllerActionOrigin_Switch_JoyConButton_E, // X
+ k_EControllerActionOrigin_Switch_JoyConButton_S, // A
+ k_EControllerActionOrigin_Switch_JoyConButton_W, // B
+
+ k_EControllerActionOrigin_PS5_LeftGrip,
+ k_EControllerActionOrigin_PS5_RightGrip,
+ k_EControllerActionOrigin_PS5_LeftFn,
+ k_EControllerActionOrigin_PS5_RightFn,
+
k_EControllerActionOrigin_Count, // If Steam has added support for new controllers origins will go here.
k_EControllerActionOrigin_MaximumPossibleValue = 32767, // Origins are currently a maximum of 16 bits.
};
@@ -529,6 +539,7 @@ typedef uint64 ControllerAnalogActionHandle_t; #define ControllerAnalogActionData_t InputAnalogActionData_t
#define ControllerDigitalActionData_t InputDigitalActionData_t
#define ControllerMotionData_t InputMotionData_t
+#define ControllerMotionDataV2_t InputMotionDataV2_t
#else
struct ControllerAnalogActionData_t
{
diff --git a/public/steam/isteamdualsense.h b/public/steam/isteamdualsense.h new file mode 100644 index 00000000..08e43708 --- /dev/null +++ b/public/steam/isteamdualsense.h @@ -0,0 +1,169 @@ +/* SIE CONFIDENTIAL
+ * $PSLibId$
+ * Copyright (C) 2019 Sony Interactive Entertainment Inc.
+ * All Rights Reserved.
+ */
+
+
+#ifndef _SCE_PAD_TRIGGER_EFFECT_H
+#define _SCE_PAD_TRIGGER_EFFECT_H
+
+
+#define SCE_PAD_TRIGGER_EFFECT_TRIGGER_MASK_L2 0x01
+#define SCE_PAD_TRIGGER_EFFECT_TRIGGER_MASK_R2 0x02
+
+#define SCE_PAD_TRIGGER_EFFECT_PARAM_INDEX_FOR_L2 0
+#define SCE_PAD_TRIGGER_EFFECT_PARAM_INDEX_FOR_R2 1
+
+#define SCE_PAD_TRIGGER_EFFECT_TRIGGER_NUM 2
+
+/* Definition of control point num */
+#define SCE_PAD_TRIGGER_EFFECT_CONTROL_POINT_NUM 10
+
+typedef enum ScePadTriggerEffectMode{
+ SCE_PAD_TRIGGER_EFFECT_MODE_OFF,
+ SCE_PAD_TRIGGER_EFFECT_MODE_FEEDBACK,
+ SCE_PAD_TRIGGER_EFFECT_MODE_WEAPON,
+ SCE_PAD_TRIGGER_EFFECT_MODE_VIBRATION,
+ SCE_PAD_TRIGGER_EFFECT_MODE_MULTIPLE_POSITION_FEEDBACK,
+ SCE_PAD_TRIGGER_EFFECT_MODE_SLOPE_FEEDBACK,
+ SCE_PAD_TRIGGER_EFFECT_MODE_MULTIPLE_POSITION_VIBRATION,
+} ScePadTriggerEffectMode;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to off mode.
+ * Off Mode: Stop trigger effect.
+ **/
+typedef struct ScePadTriggerEffectOffParam{
+ uint8_t padding[48];
+} ScePadTriggerEffectOffParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to Feedback mode.
+ * Feedback Mode: The motor arm pushes back trigger.
+ * Trigger obtains stiffness at specified position.
+ **/
+typedef struct ScePadTriggerEffectFeedbackParam{
+ uint8_t position; /*E position where the strength of target trigger start changing(0~9). */
+ uint8_t strength; /*E strength that the motor arm pushes back target trigger(0~8 (0: Same as Off mode)). */
+ uint8_t padding[46];
+} ScePadTriggerEffectFeedbackParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to Weapon mode.
+ * Weapon Mode: Emulate weapon like gun trigger.
+ **/
+typedef struct ScePadTriggerEffectWeaponParam{
+ uint8_t startPosition; /*E position where the stiffness of trigger start changing(2~7). */
+ uint8_t endPosition; /*E position where the stiffness of trigger finish changing(startPosition+1~8). */
+ uint8_t strength; /*E strength of gun trigger(0~8 (0: Same as Off mode)). */
+ uint8_t padding[45];
+} ScePadTriggerEffectWeaponParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to Vibration mode.
+ * Vibration Mode: Vibrates motor arm around specified position.
+ **/
+typedef struct ScePadTriggerEffectVibrationParam{
+ uint8_t position; /*E position where the motor arm start vibrating(0~9). */
+ uint8_t amplitude; /*E vibration amplitude(0~8 (0: Same as Off mode)). */
+ uint8_t frequency; /*E vibration frequency(0~255[Hz] (0: Same as Off mode)). */
+ uint8_t padding[45];
+} ScePadTriggerEffectVibrationParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to ScePadTriggerEffectMultiplePositionFeedbackParam mode.
+ * Multi Position Feedback Mode: The motor arm pushes back trigger.
+ * Trigger obtains specified stiffness at each control point.
+ **/
+typedef struct ScePadTriggerEffectMultiplePositionFeedbackParam{
+ uint8_t strength[SCE_PAD_TRIGGER_EFFECT_CONTROL_POINT_NUM]; /*E strength that the motor arm pushes back target trigger at position(0~8 (0: Same as Off mode)).
+ * strength[0] means strength of motor arm at position0.
+ * strength[1] means strength of motor arm at position1.
+ * ...
+ * */
+ uint8_t padding[38];
+} ScePadTriggerEffectMultiplePositionFeedbackParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to Feedback3 mode.
+ * Slope Feedback Mode: The motor arm pushes back trigger between two spedified control points.
+ * Stiffness of the trigger is changing depending on the set place.
+ **/
+typedef struct ScePadTriggerEffectSlopeFeedbackParam{
+
+ uint8_t startPosition; /*E position where the strength of target trigger start changing(0~endPosition). */
+ uint8_t endPosition; /*E position where the strength of target trigger finish changing(startPosition+1~9). */
+ uint8_t startStrength; /*E strength when trigger's position is startPosition(1~8) */
+ uint8_t endStrength; /*E strength when trigger's position is endPosition(1~8) */
+ uint8_t padding[44];
+} ScePadTriggerEffectSlopeFeedbackParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect to Vibration2 mode.
+ * Multi Position Vibration Mode: Vibrates motor arm around specified control point.
+ * Trigger vibrates specified amplitude at each control point.
+ **/
+typedef struct ScePadTriggerEffectMultiplePositionVibrationParam{
+ uint8_t frequency; /*E vibration frequency(0~255 (0: Same as Off mode)) */
+ uint8_t amplitude[SCE_PAD_TRIGGER_EFFECT_CONTROL_POINT_NUM]; /*E vibration amplitude at position(0~8 (0: Same as Off mode)).
+ * amplitude[0] means amplitude of vibration at position0.
+ * amplitude[1] means amplitude of vibration at position1.
+ * ...
+ * */
+ uint8_t padding[37];
+} ScePadTriggerEffectMultiplePositionVibrationParam;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect mode.
+ **/
+typedef union ScePadTriggerEffectCommandData{
+ ScePadTriggerEffectOffParam offParam;
+ ScePadTriggerEffectFeedbackParam feedbackParam;
+ ScePadTriggerEffectWeaponParam weaponParam;
+ ScePadTriggerEffectVibrationParam vibrationParam;
+ ScePadTriggerEffectMultiplePositionFeedbackParam multiplePositionFeedbackParam;
+ ScePadTriggerEffectSlopeFeedbackParam slopeFeedbackParam;
+ ScePadTriggerEffectMultiplePositionVibrationParam multiplePositionVibrationParam;
+} ScePadTriggerEffectCommandData;
+
+/**
+ *E
+ * @brief parameter for setting the trigger effect.
+ **/
+typedef struct ScePadTriggerEffectCommand{
+ ScePadTriggerEffectMode mode;
+ uint8_t padding[4];
+ ScePadTriggerEffectCommandData commandData;
+} ScePadTriggerEffectCommand;
+
+/**
+ *E
+ * @brief parameter for the scePadSetTriggerEffect function.
+ **/
+typedef struct ScePadTriggerEffectParam{
+
+ uint8_t triggerMask; /*E Set trigger mask to activate trigger effect commands.
+ * SCE_PAD_TRIGGER_EFFECT_TRIGGER_MASK_L2 : 0x01
+ * SCE_PAD_TRIGGER_EFFECT_TRIGGER_MASK_R2 : 0x02
+ * */
+ uint8_t padding[7];
+
+ ScePadTriggerEffectCommand command[SCE_PAD_TRIGGER_EFFECT_TRIGGER_NUM]; /*E command[SCE_PAD_TRIGGER_EFFECT_PARAM_INDEX_FOR_L2] is for L2 trigger setting
+ * and param[SCE_PAD_TRIGGER_EFFECT_PARAM_INDEX_FOR_R2] is for R2 trgger setting.
+ * */
+} ScePadTriggerEffectParam;
+
+#if defined(__cplusplus) && __cplusplus >= 201103L
+static_assert( sizeof( ScePadTriggerEffectParam ) == 120, "ScePadTriggerEffectParam has incorrect size" );
+#endif
+
+#endif /* _SCE_PAD_TRIGGER_EFFECT_H */
diff --git a/public/steam/isteamfriends.h b/public/steam/isteamfriends.h index e0a0b0fd..d57231aa 100644 --- a/public/steam/isteamfriends.h +++ b/public/steam/isteamfriends.h @@ -514,7 +514,9 @@ enum EPersonaChange struct GameOverlayActivated_t
{
enum { k_iCallback = k_iSteamFriendsCallbacks + 31 };
- uint8 m_bActive; // true if it's just been activated, false otherwise
+ uint8 m_bActive; // true if it's just been activated, false otherwise
+ bool m_bUserInitiated; // true if the user asked for the overlay to be activated/deactivated
+ AppId_t m_nAppID; // the appID of the game (should always be the current game)
};
diff --git a/public/steam/isteamgameserver.h b/public/steam/isteamgameserver.h index e7d1a1ab..73d3c4ee 100644 --- a/public/steam/isteamgameserver.h +++ b/public/steam/isteamgameserver.h @@ -152,7 +152,9 @@ public: // Retrieve ticket to be sent to the entity who wishes to authenticate you ( using BeginAuthSession API ).
// pcbTicket retrieves the length of the actual ticket.
- virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0;
+ // SteamNetworkingIdentity is an optional parameter to hold the public IP address of the entity you are connecting to
+ // if an IP address is passed Steam will only allow the ticket to be used by an entity with that IP address
+ virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, const SteamNetworkingIdentity *pSnid ) = 0;
// Authenticate ticket ( from GetAuthSessionTicket ) from entity steamID to be sure it is valid and isnt reused
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
@@ -264,7 +266,7 @@ public: )
};
-#define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer014"
+#define STEAMGAMESERVER_INTERFACE_VERSION "SteamGameServer015"
// Global accessor
inline ISteamGameServer *SteamGameServer();
diff --git a/public/steam/isteaminput.h b/public/steam/isteaminput.h index fa265ab9..0d2d48af 100644 --- a/public/steam/isteaminput.h +++ b/public/steam/isteaminput.h @@ -17,9 +17,9 @@ #define STEAM_INPUT_MAX_COUNT 16
-#define STEAM_INPUT_MAX_ANALOG_ACTIONS 16
+#define STEAM_INPUT_MAX_ANALOG_ACTIONS 24
-#define STEAM_INPUT_MAX_DIGITAL_ACTIONS 128
+#define STEAM_INPUT_MAX_DIGITAL_ACTIONS 256
#define STEAM_INPUT_MAX_ORIGINS 8
@@ -319,10 +319,10 @@ enum EInputActionOrigin k_EInputActionOrigin_Switch_LeftGrip_Upper, // Left JoyCon SL Button
k_EInputActionOrigin_Switch_RightGrip_Lower, // Right JoyCon SL Button
k_EInputActionOrigin_Switch_RightGrip_Upper, // Right JoyCon SR Button
- k_EInputActionOrigin_Switch_Reserved11,
- k_EInputActionOrigin_Switch_Reserved12,
- k_EInputActionOrigin_Switch_Reserved13,
- k_EInputActionOrigin_Switch_Reserved14,
+ k_EInputActionOrigin_Switch_JoyConButton_N, // With a Horizontal JoyCon this will be Y or what would be Dpad Right when vertical
+ k_EInputActionOrigin_Switch_JoyConButton_E, // X
+ k_EInputActionOrigin_Switch_JoyConButton_S, // A
+ k_EInputActionOrigin_Switch_JoyConButton_W, // B
k_EInputActionOrigin_Switch_Reserved15,
k_EInputActionOrigin_Switch_Reserved16,
k_EInputActionOrigin_Switch_Reserved17,
@@ -386,10 +386,10 @@ enum EInputActionOrigin k_EInputActionOrigin_PS5_Gyro_Yaw,
k_EInputActionOrigin_PS5_Gyro_Roll,
k_EInputActionOrigin_PS5_DPad_Move,
- k_EInputActionOrigin_PS5_Reserved1,
- k_EInputActionOrigin_PS5_Reserved2,
- k_EInputActionOrigin_PS5_Reserved3,
- k_EInputActionOrigin_PS5_Reserved4,
+ k_EInputActionOrigin_PS5_LeftGrip,
+ k_EInputActionOrigin_PS5_RightGrip,
+ k_EInputActionOrigin_PS5_LeftFn,
+ k_EInputActionOrigin_PS5_RightFn,
k_EInputActionOrigin_PS5_Reserved5,
k_EInputActionOrigin_PS5_Reserved6,
k_EInputActionOrigin_PS5_Reserved7,
@@ -643,12 +643,12 @@ struct InputDigitalActionData_t struct InputMotionData_t
{
- // Sensor-fused absolute rotation; will drift in heading
+ // Sensor-fused absolute rotation; will drift in heading toward average
float rotQuatX;
float rotQuatY;
float rotQuatZ;
float rotQuatW;
-
+
// Positional acceleration
float posAccelX;
float posAccelY;
@@ -660,6 +660,58 @@ struct InputMotionData_t float rotVelZ;
};
+
+struct InputMotionDataV2_t
+{
+ //
+ // Gyro post processing:
+ //
+
+ // Drift Corrected Quaternion is calculated after steam input controller calibration values have been applied.
+ // Rawest _useful_ version of a quaternion.
+ // Most camera implementations should use this by comparing last rotation against current rotation, and applying the difference to the in game camera (plus your own sensitivity tweaks)
+ // It is worth viewing
+ float driftCorrectedQuatX;
+ float driftCorrectedQuatY;
+ float driftCorrectedQuatZ;
+ float driftCorrectedQuatW;
+
+ // Sensor fusion corrects using accelerometer, and "average forward over time" for "forward".
+ // This can "ouija" your aim, so it's not so appropriate for camera controls (sensor fusion was originally made for racing game steering )
+ // Same result as from old InputMotionData_t::rotQuatX/Y/Z/W
+ float sensorFusionQuatX;
+ float sensorFusionQuatY;
+ float sensorFusionQuatZ;
+ float sensorFusionQuatW;
+
+ // Deferred Sensor fusion quaternion with deferred correction
+ // Reduces perception of "ouija" effect by only applying correction when the controller is below "low noise" thresholds,
+ // while the controller rotates fast - never when the user is attempting precision aim.
+ float deferredSensorFusionQuatX;
+ float deferredSensorFusionQuatY;
+ float deferredSensorFusionQuatZ;
+ float deferredSensorFusionQuatW;
+
+ // Same as accel but values are calibrated such that 1 unit = 1G.
+ // X = Right
+ // Y = Forward out through the joystick USB port.
+ // Z = Up through the joystick axis.
+ float gravityX;
+ float gravityY;
+ float gravityZ;
+
+ //
+ // Same as rotVel values in GetMotionData but values are calibrated to degrees per second.
+ // Local Space (controller relative)
+ // X = Pitch = left to right axis
+ // Y = Roll = axis through charging port
+ // Z = Yaw = axis through sticks
+ float degreesPerSecondX;
+ float degreesPerSecondY;
+ float degreesPerSecondZ;
+
+};
+
//-----------------------------------------------------------------------------
// Purpose: when callbacks are enabled this fires each time a controller action
// state changes
@@ -682,6 +734,11 @@ struct SteamInputActionEvent_t };
};
+//-----------------------------------------------------------------------------
+// Forward declaration for ScePadTriggerEffectParam, defined in isteamdualsense.h
+//-----------------------------------------------------------------------------
+struct ScePadTriggerEffectParam;
+
#pragma pack( pop )
typedef void ( *SteamInputActionEventCallbackPointer )( SteamInputActionEvent_t * );
@@ -892,9 +949,12 @@ public: // See isteamremoteplay.h for more information on Steam Remote Play sessions
virtual uint32 GetRemotePlaySessionID( InputHandle_t inputHandle ) = 0;
- // Get a bitmask of the Steam Input Configuration types opted in for the current session. Returns ESteamInputConfigurationEnableType values.?
+ // Get a bitmask of the Steam Input Configuration types opted in for the current session. Returns ESteamInputConfigurationEnableType values.
// Note: user can override the settings from the Steamworks Partner site so the returned values may not exactly match your default configuration
virtual uint16 GetSessionInputConfigurationSettings() = 0;
+
+ // Set the trigger effect for a DualSense controller
+ virtual void SetDualSenseTriggerEffect( InputHandle_t inputHandle, const ScePadTriggerEffectParam *pParam ) = 0;
};
#define STEAMINPUT_INTERFACE_VERSION "SteamInput006"
@@ -951,4 +1011,4 @@ struct SteamInputConfigurationLoaded_t #pragma pack( pop )
-#endif // ISTEAMINPUT_H
\ No newline at end of file +#endif // ISTEAMINPUT_H
diff --git a/public/steam/isteamparentalsettings.h b/public/steam/isteamparentalsettings.h index 66e18a06..ad435875 100644 --- a/public/steam/isteamparentalsettings.h +++ b/public/steam/isteamparentalsettings.h @@ -29,6 +29,7 @@ enum EParentalFeature k_EFeatureLibrary = 11,
k_EFeatureTest = 12,
k_EFeatureSiteLicense = 13,
+ k_EFeatureKioskMode = 14,
k_EFeatureMax
};
diff --git a/public/steam/isteamugc.h b/public/steam/isteamugc.h index 347e119b..560258ac 100644 --- a/public/steam/isteamugc.h +++ b/public/steam/isteamugc.h @@ -157,6 +157,15 @@ enum EItemPreviewType k_EItemPreviewType_ReservedMax = 255, // you can specify your own types above this value
};
+enum EUGCContentDescriptorID
+{
+ k_EUGCContentDescriptor_NudityOrSexualContent = 1,
+ k_EUGCContentDescriptor_FrequentViolenceOrGore = 2,
+ k_EUGCContentDescriptor_AdultOnlySexualContent = 3,
+ k_EUGCContentDescriptor_GratuitousSexualContent = 4,
+ k_EUGCContentDescriptor_AnyMatureContent = 5,
+};
+
const uint32 kNumUGCResultsPerPage = 50;
const uint32 k_cchDeveloperMetadataMax = 5000;
@@ -237,6 +246,8 @@ public: STEAM_FLAT_NAME( GetQueryFirstUGCKeyValueTag )
virtual bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, const char *pchKey, STEAM_OUT_STRING_COUNT(cchValueSize) char *pchValue, uint32 cchValueSize ) = 0;
+ virtual uint32 GetQueryUGCContentDescriptors( UGCQueryHandle_t handle, uint32 index, EUGCContentDescriptorID *pvecDescriptors, uint32 cMaxEntries ) = 0;
+
// Release the request to free up memory, after retrieving results
virtual bool ReleaseQueryUGCRequest( UGCQueryHandle_t handle ) = 0;
@@ -293,6 +304,8 @@ public: virtual bool UpdateItemPreviewFile( UGCUpdateHandle_t handle, uint32 index, const char *pszPreviewFile ) = 0; // updates an existing preview file for this item. pszPreviewFile points to local file, which must be under 1MB in size
virtual bool UpdateItemPreviewVideo( UGCUpdateHandle_t handle, uint32 index, const char *pszVideoID ) = 0; // updates an existing preview video for this item
virtual bool RemoveItemPreview( UGCUpdateHandle_t handle, uint32 index ) = 0; // remove a preview by index starting at 0 (previews are sorted)
+ virtual bool AddContentDescriptor( UGCUpdateHandle_t handle, EUGCContentDescriptorID descid ) = 0;
+ virtual bool RemoveContentDescriptor( UGCUpdateHandle_t handle, EUGCContentDescriptorID descid ) = 0;
STEAM_CALL_RESULT( SubmitItemUpdateResult_t )
virtual SteamAPICall_t SubmitItemUpdate( UGCUpdateHandle_t handle, const char *pchChangeNote ) = 0; // commit update process started with StartItemUpdate()
@@ -371,7 +384,7 @@ public: virtual SteamAPICall_t GetWorkshopEULAStatus() = 0;
};
-#define STEAMUGC_INTERFACE_VERSION "STEAMUGC_INTERFACE_VERSION016"
+#define STEAMUGC_INTERFACE_VERSION "STEAMUGC_INTERFACE_VERSION017"
// Global interface accessor
inline ISteamUGC *SteamUGC();
diff --git a/public/steam/isteamuser.h b/public/steam/isteamuser.h index 54b9fa36..fc144836 100644 --- a/public/steam/isteamuser.h +++ b/public/steam/isteamuser.h @@ -124,7 +124,10 @@ public: // Retrieve ticket to be sent to the entity who wishes to authenticate you.
// pcbTicket retrieves the length of the actual ticket.
- virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) = 0;
+ // SteamNetworkingIdentity is an optional input parameter to hold the public IP address or SteamID of the entity you are connecting to
+ // if an IP address is passed Steam will only allow the ticket to be used by an entity with that IP address
+ // if a Steam ID is passed Steam will only allow the ticket to be used by that Steam ID
+ virtual HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket, const SteamNetworkingIdentity *pSteamNetworkingIdentity ) = 0;
// Authenticate ticket from entity steamID to be sure it is valid and isnt reused
// Registers for callbacks if the entity goes offline or cancels the ticket ( see ValidateAuthTicketResponse_t callback and EAuthSessionResponse )
@@ -210,7 +213,7 @@ public: };
-#define STEAMUSER_INTERFACE_VERSION "SteamUser021"
+#define STEAMUSER_INTERFACE_VERSION "SteamUser022"
// Global interface accessor
inline ISteamUser *SteamUser();
@@ -225,12 +228,13 @@ STEAM_DEFINE_USER_INTERFACE_ACCESSOR( ISteamUser *, SteamUser, STEAMUSER_INTERFA #error steam_api_common.h should define VALVE_CALLBACK_PACK_xxx
#endif
+
//-----------------------------------------------------------------------------
-// Purpose: called when a connections to the Steam back-end has been established
-// this means the Steam client now has a working connection to the Steam servers
-// usually this will have occurred before the game has launched, and should
+// Purpose: Called when an authenticated connection to the Steam back-end has been established.
+// This means the Steam client now has a working connection to the Steam servers.
+// Usually this will have occurred before the game has launched, and should
// only be seen if the user has dropped connection due to a networking issue
-// or a Steam server update
+// or a Steam server update.
//-----------------------------------------------------------------------------
struct SteamServersConnected_t
{
diff --git a/public/steam/isteamutils.h b/public/steam/isteamutils.h index d841f409..94743212 100644 --- a/public/steam/isteamutils.h +++ b/public/steam/isteamutils.h @@ -6,9 +6,6 @@ #ifndef ISTEAMUTILS_H
#define ISTEAMUTILS_H
-#ifdef _WIN32
-#pragma once
-#endif
#include "steam_api_common.h"
@@ -60,12 +57,6 @@ enum ETextFilteringContext };
-// function prototype for warning message hook
-#if defined( POSIX )
-#define __cdecl
-#endif
-extern "C" typedef void (__cdecl *SteamAPIWarningMessageHook_t)(int, const char *);
-
//-----------------------------------------------------------------------------
// Purpose: interface to user independent utility functions
//-----------------------------------------------------------------------------
@@ -317,6 +308,7 @@ struct GamepadTextInputDismissed_t enum { k_iCallback = k_iSteamUtilsCallbacks + 14 };
bool m_bSubmitted; // true if user entered & accepted text (Call ISteamUtils::GetEnteredGamepadTextInput() for text), false if canceled input
uint32 m_unSubmittedText;
+ AppId_t m_unAppID;
};
// k_iSteamUtilsCallbacks + 15 through 35 are taken
@@ -334,6 +326,15 @@ struct FloatingGamepadTextInputDismissed_t enum { k_iCallback = k_iSteamUtilsCallbacks + 38 };
};
+//-----------------------------------------------------------------------------
+// The text filtering dictionary has changed
+//-----------------------------------------------------------------------------
+struct FilterTextDictionaryChanged_t
+{
+ enum { k_iCallback = k_iSteamUtilsCallbacks + 39 };
+ int m_eLanguage; // One of ELanguage, or k_LegallyRequiredFiltering
+};
+
#pragma pack( pop )
#endif // ISTEAMUTILS_H
diff --git a/public/steam/lib/linux32/libsdkencryptedappticket.so b/public/steam/lib/linux32/libsdkencryptedappticket.so Binary files differindex 226554ea..2ec9bd8a 100644 --- a/public/steam/lib/linux32/libsdkencryptedappticket.so +++ b/public/steam/lib/linux32/libsdkencryptedappticket.so diff --git a/public/steam/lib/linux64/libsdkencryptedappticket.so b/public/steam/lib/linux64/libsdkencryptedappticket.so Binary files differindex 6250125f..fe77ac24 100644 --- a/public/steam/lib/linux64/libsdkencryptedappticket.so +++ b/public/steam/lib/linux64/libsdkencryptedappticket.so diff --git a/public/steam/lib/osx/libsdkencryptedappticket.dylib b/public/steam/lib/osx/libsdkencryptedappticket.dylib Binary files differindex 40787f17..6e785d4a 100644 --- a/public/steam/lib/osx/libsdkencryptedappticket.dylib +++ b/public/steam/lib/osx/libsdkencryptedappticket.dylib diff --git a/public/steam/matchmakingtypes.h b/public/steam/matchmakingtypes.h index 6446a573..b15af3fa 100644 --- a/public/steam/matchmakingtypes.h +++ b/public/steam/matchmakingtypes.h @@ -8,16 +8,6 @@ #ifndef MATCHMAKINGTYPES_H
#define MATCHMAKINGTYPES_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#ifdef POSIX
-#ifndef _snprintf
-#define _snprintf snprintf
-#endif
-#endif
-
#include <stdio.h>
#include <string.h>
@@ -68,9 +58,6 @@ public: servernetadr_t() : m_usConnectionPort( 0 ), m_usQueryPort( 0 ), m_unIP( 0 ) {}
void Init( unsigned int ip, uint16 usQueryPort, uint16 usConnectionPort );
-#ifdef NETADR_H
- netadr_t GetIPAndQueryPort();
-#endif
// Access the query port.
uint16 GetQueryPort() const;
@@ -113,13 +100,6 @@ inline void servernetadr_t::Init( unsigned int ip, uint16 usQueryPort, uint16 us m_usConnectionPort = usConnectionPort;
}
-#ifdef NETADR_H
-inline netadr_t servernetadr_t::GetIPAndQueryPort()
-{
- return netadr_t( m_unIP, m_usQueryPort );
-}
-#endif
-
inline uint16 servernetadr_t::GetQueryPort() const
{
return m_usQueryPort;
@@ -156,9 +136,9 @@ inline const char *servernetadr_t::ToString( uint32 unIP, uint16 usPort ) const static int nBuf = 0;
unsigned char *ipByte = (unsigned char *)&unIP;
#ifdef VALVE_BIG_ENDIAN
- _snprintf (s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[0]), (int)(ipByte[1]), (int)(ipByte[2]), (int)(ipByte[3]), usPort );
+ snprintf(s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[0]), (int)(ipByte[1]), (int)(ipByte[2]), (int)(ipByte[3]), usPort );
#else
- _snprintf (s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[3]), (int)(ipByte[2]), (int)(ipByte[1]), (int)(ipByte[0]), usPort );
+ snprintf(s[nBuf], sizeof( s[nBuf] ), "%u.%u.%u.%u:%i", (int)(ipByte[3]), (int)(ipByte[2]), (int)(ipByte[1]), (int)(ipByte[0]), usPort );
#endif
const char *pchRet = s[nBuf];
++nBuf;
diff --git a/public/steam/steam_api.json b/public/steam/steam_api.json index 5ae79694..c93ba668 100644 --- a/public/steam/steam_api.json +++ b/public/steam/steam_api.json @@ -136,7 +136,9 @@ {
"callback_id": 331,
"fields": [
- { "fieldname":"m_bActive", "fieldtype":"uint8" }
+ { "fieldname":"m_bActive", "fieldtype":"uint8" },
+ { "fieldname":"m_bUserInitiated", "fieldtype":"bool" },
+ { "fieldname":"m_nAppID", "fieldtype":"AppId_t" }
],
"struct": "GameOverlayActivated_t"
},
@@ -347,7 +349,8 @@ "callback_id": 714,
"fields": [
{ "fieldname":"m_bSubmitted", "fieldtype":"bool" },
- { "fieldname":"m_unSubmittedText", "fieldtype":"uint32" }
+ { "fieldname":"m_unSubmittedText", "fieldtype":"uint32" },
+ { "fieldname":"m_unAppID", "fieldtype":"AppId_t" }
],
"struct": "GamepadTextInputDismissed_t"
},
@@ -362,6 +365,13 @@ "struct": "FloatingGamepadTextInputDismissed_t"
},
{
+ "callback_id": 739,
+ "fields": [
+ { "fieldname":"m_eLanguage", "fieldtype":"int" }
+ ],
+ "struct": "FilterTextDictionaryChanged_t"
+ },
+ {
"callback_id": 502,
"fields": [
{ "fieldname":"m_nIP", "fieldtype":"uint32" },
@@ -969,14 +979,6 @@ "struct": "DlcInstalled_t"
},
{
- "callback_id": 1008,
- "fields": [
- { "fieldname":"m_eResult", "fieldtype":"ERegisterActivationCodeResult" },
- { "fieldname":"m_unPackageRegistered", "fieldtype":"uint32" }
- ],
- "struct": "RegisterActivationCodeResponse_t"
- },
- {
"callback_id": 1014,
"fields": [],
"struct": "NewUrlLaunchParameters_t"
@@ -2099,7 +2101,8 @@ { "name":"k_EResultNoVerifiedPhone", "value":"123" },
{ "name":"k_EResultInsufficientBattery", "value":"124" },
{ "name":"k_EResultChargerRequired", "value":"125" },
- { "name":"k_EResultCachedCredentialInvalid", "value":"126" }
+ { "name":"k_EResultCachedCredentialInvalid", "value":"126" },
+ { "name":"K_EResultPhoneNumberIsVOIP", "value":"127" }
]
},
{
@@ -2161,7 +2164,8 @@ { "name":"k_EAuthSessionResponseAuthTicketCanceled", "value":"6" },
{ "name":"k_EAuthSessionResponseAuthTicketInvalidAlreadyUsed", "value":"7" },
{ "name":"k_EAuthSessionResponseAuthTicketInvalid", "value":"8" },
- { "name":"k_EAuthSessionResponsePublisherIssuedBan", "value":"9" }
+ { "name":"k_EAuthSessionResponsePublisherIssuedBan", "value":"9" },
+ { "name":"k_EAuthSessionResponseAuthTicketNetworkIdentityFailure", "value":"10" }
]
},
{
@@ -2235,6 +2239,7 @@ {
"enumname": "ENotificationPosition",
"values": [
+ { "name":"k_EPositionInvalid", "value":"-1" },
{ "name":"k_EPositionTopLeft", "value":"0" },
{ "name":"k_EPositionTopRight", "value":"1" },
{ "name":"k_EPositionBottomLeft", "value":"2" },
@@ -2743,16 +2748,6 @@ ]
},
{
- "enumname": "ERegisterActivationCodeResult",
- "values": [
- { "name":"k_ERegisterActivationCodeResultOK", "value":"0" },
- { "name":"k_ERegisterActivationCodeResultFail", "value":"1" },
- { "name":"k_ERegisterActivationCodeResultAlreadyRegistered", "value":"2" },
- { "name":"k_ERegisterActivationCodeResultTimeout", "value":"3" },
- { "name":"k_ERegisterActivationCodeAlreadyOwned", "value":"4" }
- ]
- },
- {
"enumname": "EP2PSessionError",
"values": [
{ "name":"k_EP2PSessionErrorNone", "value":"0" },
@@ -2849,6 +2844,7 @@ { "name":"k_EHTTPStatusCode304NotModified", "value":"304" },
{ "name":"k_EHTTPStatusCode305UseProxy", "value":"305" },
{ "name":"k_EHTTPStatusCode307TemporaryRedirect", "value":"307" },
+ { "name":"k_EHTTPStatusCode308PermanentRedirect", "value":"308" },
{ "name":"k_EHTTPStatusCode400BadRequest", "value":"400" },
{ "name":"k_EHTTPStatusCode401Unauthorized", "value":"401" },
{ "name":"k_EHTTPStatusCode402PaymentRequired", "value":"402" },
@@ -3152,10 +3148,10 @@ { "name":"k_EInputActionOrigin_Switch_LeftGrip_Upper", "value":"245" },
{ "name":"k_EInputActionOrigin_Switch_RightGrip_Lower", "value":"246" },
{ "name":"k_EInputActionOrigin_Switch_RightGrip_Upper", "value":"247" },
- { "name":"k_EInputActionOrigin_Switch_Reserved11", "value":"248" },
- { "name":"k_EInputActionOrigin_Switch_Reserved12", "value":"249" },
- { "name":"k_EInputActionOrigin_Switch_Reserved13", "value":"250" },
- { "name":"k_EInputActionOrigin_Switch_Reserved14", "value":"251" },
+ { "name":"k_EInputActionOrigin_Switch_JoyConButton_N", "value":"248" },
+ { "name":"k_EInputActionOrigin_Switch_JoyConButton_E", "value":"249" },
+ { "name":"k_EInputActionOrigin_Switch_JoyConButton_S", "value":"250" },
+ { "name":"k_EInputActionOrigin_Switch_JoyConButton_W", "value":"251" },
{ "name":"k_EInputActionOrigin_Switch_Reserved15", "value":"252" },
{ "name":"k_EInputActionOrigin_Switch_Reserved16", "value":"253" },
{ "name":"k_EInputActionOrigin_Switch_Reserved17", "value":"254" },
@@ -3217,10 +3213,10 @@ { "name":"k_EInputActionOrigin_PS5_Gyro_Yaw", "value":"310" },
{ "name":"k_EInputActionOrigin_PS5_Gyro_Roll", "value":"311" },
{ "name":"k_EInputActionOrigin_PS5_DPad_Move", "value":"312" },
- { "name":"k_EInputActionOrigin_PS5_Reserved1", "value":"313" },
- { "name":"k_EInputActionOrigin_PS5_Reserved2", "value":"314" },
- { "name":"k_EInputActionOrigin_PS5_Reserved3", "value":"315" },
- { "name":"k_EInputActionOrigin_PS5_Reserved4", "value":"316" },
+ { "name":"k_EInputActionOrigin_PS5_LeftGrip", "value":"313" },
+ { "name":"k_EInputActionOrigin_PS5_RightGrip", "value":"314" },
+ { "name":"k_EInputActionOrigin_PS5_LeftFn", "value":"315" },
+ { "name":"k_EInputActionOrigin_PS5_RightFn", "value":"316" },
{ "name":"k_EInputActionOrigin_PS5_Reserved5", "value":"317" },
{ "name":"k_EInputActionOrigin_PS5_Reserved6", "value":"318" },
{ "name":"k_EInputActionOrigin_PS5_Reserved7", "value":"319" },
@@ -3817,7 +3813,15 @@ { "name":"k_EControllerActionOrigin_SteamDeck_Reserved18", "value":"375" },
{ "name":"k_EControllerActionOrigin_SteamDeck_Reserved19", "value":"376" },
{ "name":"k_EControllerActionOrigin_SteamDeck_Reserved20", "value":"377" },
- { "name":"k_EControllerActionOrigin_Count", "value":"378" },
+ { "name":"k_EControllerActionOrigin_Switch_JoyConButton_N", "value":"378" },
+ { "name":"k_EControllerActionOrigin_Switch_JoyConButton_E", "value":"379" },
+ { "name":"k_EControllerActionOrigin_Switch_JoyConButton_S", "value":"380" },
+ { "name":"k_EControllerActionOrigin_Switch_JoyConButton_W", "value":"381" },
+ { "name":"k_EControllerActionOrigin_PS5_LeftGrip", "value":"382" },
+ { "name":"k_EControllerActionOrigin_PS5_RightGrip", "value":"383" },
+ { "name":"k_EControllerActionOrigin_PS5_LeftFn", "value":"384" },
+ { "name":"k_EControllerActionOrigin_PS5_RightFn", "value":"385" },
+ { "name":"k_EControllerActionOrigin_Count", "value":"386" },
{ "name":"k_EControllerActionOrigin_MaximumPossibleValue", "value":"32767" }
]
},
@@ -3951,6 +3955,16 @@ ]
},
{
+ "enumname": "EUGCContentDescriptorID",
+ "values": [
+ { "name":"k_EUGCContentDescriptor_NudityOrSexualContent", "value":"1" },
+ { "name":"k_EUGCContentDescriptor_FrequentViolenceOrGore", "value":"2" },
+ { "name":"k_EUGCContentDescriptor_AdultOnlySexualContent", "value":"3" },
+ { "name":"k_EUGCContentDescriptor_GratuitousSexualContent", "value":"4" },
+ { "name":"k_EUGCContentDescriptor_AnyMatureContent", "value":"5" }
+ ]
+ },
+ {
"enumname": "ESteamItemFlags",
"values": [
{ "name":"k_ESteamItemNoTrade", "value":"1" },
@@ -3975,7 +3989,8 @@ { "name":"k_EFeatureLibrary", "value":"11" },
{ "name":"k_EFeatureTest", "value":"12" },
{ "name":"k_EFeatureSiteLicense", "value":"13" },
- { "name":"k_EFeatureMax", "value":"14" }
+ { "name":"k_EFeatureKioskMode", "value":"14" },
+ { "name":"k_EFeatureMax", "value":"15" }
]
},
{
@@ -4555,7 +4570,7 @@ {
"kind": "user",
"name": "SteamUser",
- "name_flat": "SteamAPI_SteamUser_v021"
+ "name_flat": "SteamAPI_SteamUser_v022"
}
],
"classname": "ISteamUser",
@@ -4684,7 +4699,8 @@ "params": [
{ "paramname":"pTicket", "paramtype":"void *" },
{ "paramname":"cbMaxTicket", "paramtype":"int" },
- { "paramname":"pcbTicket", "paramtype":"uint32 *" }
+ { "paramname":"pcbTicket", "paramtype":"uint32 *" },
+ { "paramname":"pSteamNetworkingIdentity", "paramtype":"const SteamNetworkingIdentity *" }
],
"returntype": "HAuthTicket"
},
@@ -4830,7 +4846,7 @@ "returntype": "bool"
}
],
- "version_string": "SteamUser021"
+ "version_string": "SteamUser022"
},
{
"accessors": [
@@ -8026,6 +8042,14 @@ { "paramname":"punSecondsPlayed", "paramtype":"uint32 *" }
],
"returntype": "bool"
+ },
+ {
+ "methodname": "SetDlcContext",
+ "methodname_flat": "SteamAPI_ISteamApps_SetDlcContext",
+ "params": [
+ { "paramname":"nAppID", "paramtype":"AppId_t" }
+ ],
+ "returntype": "bool"
}
],
"version_string": "STEAMAPPS_INTERFACE_VERSION008"
@@ -9378,6 +9402,15 @@ "methodname_flat": "SteamAPI_ISteamInput_GetSessionInputConfigurationSettings",
"params": [],
"returntype": "uint16"
+ },
+ {
+ "methodname": "SetDualSenseTriggerEffect",
+ "methodname_flat": "SteamAPI_ISteamInput_SetDualSenseTriggerEffect",
+ "params": [
+ { "paramname":"inputHandle", "paramtype":"InputHandle_t" },
+ { "paramname":"pParam", "paramtype":"const ScePadTriggerEffectParam *" }
+ ],
+ "returntype": "void"
}
],
"version_string": "SteamInput006"
@@ -9717,12 +9750,12 @@ {
"kind": "user",
"name": "SteamUGC",
- "name_flat": "SteamAPI_SteamUGC_v016"
+ "name_flat": "SteamAPI_SteamUGC_v017"
},
{
"kind": "gameserver",
"name": "SteamGameServerUGC",
- "name_flat": "SteamAPI_SteamGameServerUGC_v016"
+ "name_flat": "SteamAPI_SteamGameServerUGC_v017"
}
],
"classname": "ISteamUGC",
@@ -9967,6 +10000,17 @@ "returntype": "bool"
},
{
+ "methodname": "GetQueryUGCContentDescriptors",
+ "methodname_flat": "SteamAPI_ISteamUGC_GetQueryUGCContentDescriptors",
+ "params": [
+ { "paramname":"handle", "paramtype":"UGCQueryHandle_t" },
+ { "paramname":"index", "paramtype":"uint32" },
+ { "paramname":"pvecDescriptors", "paramtype":"EUGCContentDescriptorID *" },
+ { "paramname":"cMaxEntries", "paramtype":"uint32" }
+ ],
+ "returntype": "uint32"
+ },
+ {
"methodname": "ReleaseQueryUGCRequest",
"methodname_flat": "SteamAPI_ISteamUGC_ReleaseQueryUGCRequest",
"params": [
@@ -10343,6 +10387,24 @@ "returntype": "bool"
},
{
+ "methodname": "AddContentDescriptor",
+ "methodname_flat": "SteamAPI_ISteamUGC_AddContentDescriptor",
+ "params": [
+ { "paramname":"handle", "paramtype":"UGCUpdateHandle_t" },
+ { "paramname":"descid", "paramtype":"EUGCContentDescriptorID" }
+ ],
+ "returntype": "bool"
+ },
+ {
+ "methodname": "RemoveContentDescriptor",
+ "methodname_flat": "SteamAPI_ISteamUGC_RemoveContentDescriptor",
+ "params": [
+ { "paramname":"handle", "paramtype":"UGCUpdateHandle_t" },
+ { "paramname":"descid", "paramtype":"EUGCContentDescriptorID" }
+ ],
+ "returntype": "bool"
+ },
+ {
"callresult": "SubmitItemUpdateResult_t",
"methodname": "SubmitItemUpdate",
"methodname_flat": "SteamAPI_ISteamUGC_SubmitItemUpdate",
@@ -10593,7 +10655,7 @@ "returntype": "SteamAPICall_t"
}
],
- "version_string": "STEAMUGC_INTERFACE_VERSION016"
+ "version_string": "STEAMUGC_INTERFACE_VERSION017"
},
{
"accessors": [
@@ -12657,7 +12719,7 @@ {
"kind": "gameserver",
"name": "SteamGameServer",
- "name_flat": "SteamAPI_SteamGameServer_v014"
+ "name_flat": "SteamAPI_SteamGameServer_v015"
}
],
"classname": "ISteamGameServer",
@@ -12849,7 +12911,8 @@ "params": [
{ "paramname":"pTicket", "paramtype":"void *" },
{ "paramname":"cbMaxTicket", "paramtype":"int" },
- { "paramname":"pcbTicket", "paramtype":"uint32 *" }
+ { "paramname":"pcbTicket", "paramtype":"uint32 *" },
+ { "paramname":"pSnid", "paramtype":"const SteamNetworkingIdentity *" }
],
"returntype": "HAuthTicket"
},
@@ -12993,7 +13056,7 @@ "returntype": "bool"
}
],
- "version_string": "SteamGameServer014"
+ "version_string": "SteamGameServer015"
},
{
"accessors": [
@@ -13415,6 +13478,29 @@ },
{
"fields": [
+ { "fieldname":"driftCorrectedQuatX", "fieldtype":"float" },
+ { "fieldname":"driftCorrectedQuatY", "fieldtype":"float" },
+ { "fieldname":"driftCorrectedQuatZ", "fieldtype":"float" },
+ { "fieldname":"driftCorrectedQuatW", "fieldtype":"float" },
+ { "fieldname":"sensorFusionQuatX", "fieldtype":"float" },
+ { "fieldname":"sensorFusionQuatY", "fieldtype":"float" },
+ { "fieldname":"sensorFusionQuatZ", "fieldtype":"float" },
+ { "fieldname":"sensorFusionQuatW", "fieldtype":"float" },
+ { "fieldname":"deferredSensorFusionQuatX", "fieldtype":"float" },
+ { "fieldname":"deferredSensorFusionQuatY", "fieldtype":"float" },
+ { "fieldname":"deferredSensorFusionQuatZ", "fieldtype":"float" },
+ { "fieldname":"deferredSensorFusionQuatW", "fieldtype":"float" },
+ { "fieldname":"gravityX", "fieldtype":"float" },
+ { "fieldname":"gravityY", "fieldtype":"float" },
+ { "fieldname":"gravityZ", "fieldtype":"float" },
+ { "fieldname":"degreesPerSecondX", "fieldtype":"float" },
+ { "fieldname":"degreesPerSecondY", "fieldtype":"float" },
+ { "fieldname":"degreesPerSecondZ", "fieldtype":"float" }
+ ],
+ "struct": "InputMotionDataV2_t"
+ },
+ {
+ "fields": [
{ "fieldname":"controllerHandle", "fieldtype":"InputHandle_t" },
{ "fieldname":"eEventType", "fieldtype":"ESteamInputActionEventType" },
{ "fieldname":"analogAction", "fieldtype":"SteamInputActionEvent_t::AnalogAction_t" }
diff --git a/public/steam/steam_api_common.h b/public/steam/steam_api_common.h index 7620a8de..f8e80d1a 100644 --- a/public/steam/steam_api_common.h +++ b/public/steam/steam_api_common.h @@ -9,9 +9,6 @@ #ifndef STEAM_API_COMMON_H
#define STEAM_API_COMMON_H
-#ifdef _WIN32
-#pragma once
-#endif
#include "steamtypes.h"
#include "steamclientpublic.h"
@@ -25,7 +22,7 @@ #else
#define S_API extern "C" __declspec( dllimport )
#endif // STEAM_API_EXPORTS
-#elif defined( GNUC )
+#elif defined( __GNUC__ )
#if defined( STEAM_API_EXPORTS )
#define S_API extern "C" __attribute__ ((visibility("default")))
#else
@@ -51,12 +48,18 @@ typedef int32 HSteamPipe;
// handle to single instance of a steam user
typedef int32 HSteamUser;
-// function prototype
-#if defined( POSIX )
-#define __cdecl
+
+// #define away __cdecl on posix.
+// This is really, really bad. We're sorry. But it's been this way for
+// a long time now and it's scary to change it, as there may be others that
+// depend on it.
+#ifndef _WIN32
+ #define __cdecl
#endif
-extern "C" typedef void (__cdecl *SteamAPIWarningMessageHook_t)(int, const char *);
-extern "C" typedef uint32 ( *SteamAPI_CheckCallbackRegistered_t )( int iCallbackNum );
+
+// function prototype
+extern "C" typedef void ( S_CALLTYPE *SteamAPIWarningMessageHook_t )( int, const char * );
+extern "C" typedef uint32 ( S_CALLTYPE *SteamAPI_CheckCallbackRegistered_t )( int iCallbackNum );
#if defined( __SNC__ )
#pragma diag_suppress=1700 // warning 1700: class "%s" has virtual functions but non-virtual destructor
#endif
diff --git a/public/steam/steam_api_flat.h b/public/steam/steam_api_flat.h index 02aaac94..9e851799 100644 --- a/public/steam/steam_api_flat.h +++ b/public/steam/steam_api_flat.h @@ -58,9 +58,9 @@ S_API ISteamRemotePlay * SteamAPI_ISteamClient_GetISteamRemotePlay( ISteamClient // ISteamUser
// A versioned accessor is exported by the library
-S_API ISteamUser *SteamAPI_SteamUser_v021();
+S_API ISteamUser *SteamAPI_SteamUser_v022();
// Inline, unversioned accessor to get the current version. Essentially the same as SteamUser(), but using this ensures that you are using a matching library.
-inline ISteamUser *SteamAPI_SteamUser() { return SteamAPI_SteamUser_v021(); }
+inline ISteamUser *SteamAPI_SteamUser() { return SteamAPI_SteamUser_v022(); }
S_API HSteamUser SteamAPI_ISteamUser_GetHSteamUser( ISteamUser* self );
S_API bool SteamAPI_ISteamUser_BLoggedOn( ISteamUser* self );
S_API uint64_steamid SteamAPI_ISteamUser_GetSteamID( ISteamUser* self );
@@ -74,7 +74,7 @@ S_API EVoiceResult SteamAPI_ISteamUser_GetAvailableVoice( ISteamUser* self, uint S_API EVoiceResult SteamAPI_ISteamUser_GetVoice( ISteamUser* self, bool bWantCompressed, void * pDestBuffer, uint32 cbDestBufferSize, uint32 * nBytesWritten, bool bWantUncompressed_Deprecated, void * pUncompressedDestBuffer_Deprecated, uint32 cbUncompressedDestBufferSize_Deprecated, uint32 * nUncompressBytesWritten_Deprecated, uint32 nUncompressedVoiceDesiredSampleRate_Deprecated );
S_API EVoiceResult SteamAPI_ISteamUser_DecompressVoice( ISteamUser* self, const void * pCompressed, uint32 cbCompressed, void * pDestBuffer, uint32 cbDestBufferSize, uint32 * nBytesWritten, uint32 nDesiredSampleRate );
S_API uint32 SteamAPI_ISteamUser_GetVoiceOptimalSampleRate( ISteamUser* self );
-S_API HAuthTicket SteamAPI_ISteamUser_GetAuthSessionTicket( ISteamUser* self, void * pTicket, int cbMaxTicket, uint32 * pcbTicket );
+S_API HAuthTicket SteamAPI_ISteamUser_GetAuthSessionTicket( ISteamUser* self, void * pTicket, int cbMaxTicket, uint32 * pcbTicket, const SteamNetworkingIdentity * pSteamNetworkingIdentity );
S_API EBeginAuthSessionResult SteamAPI_ISteamUser_BeginAuthSession( ISteamUser* self, const void * pAuthTicket, int cbAuthTicket, uint64_steamid steamID );
S_API void SteamAPI_ISteamUser_EndAuthSession( ISteamUser* self, uint64_steamid steamID );
S_API void SteamAPI_ISteamUser_CancelAuthTicket( ISteamUser* self, HAuthTicket hAuthTicket );
@@ -510,6 +510,7 @@ S_API SteamAPICall_t SteamAPI_ISteamApps_GetFileDetails( ISteamApps* self, const S_API int SteamAPI_ISteamApps_GetLaunchCommandLine( ISteamApps* self, char * pszCommandLine, int cubCommandLine );
S_API bool SteamAPI_ISteamApps_BIsSubscribedFromFamilySharing( ISteamApps* self );
S_API bool SteamAPI_ISteamApps_BIsTimedTrial( ISteamApps* self, uint32 * punSecondsAllowed, uint32 * punSecondsPlayed );
+S_API bool SteamAPI_ISteamApps_SetDlcContext( ISteamApps* self, AppId_t nAppID );
// ISteamNetworking
@@ -706,6 +707,7 @@ S_API EInputActionOrigin SteamAPI_ISteamInput_TranslateActionOrigin( ISteamInput S_API bool SteamAPI_ISteamInput_GetDeviceBindingRevision( ISteamInput* self, InputHandle_t inputHandle, int * pMajor, int * pMinor );
S_API uint32 SteamAPI_ISteamInput_GetRemotePlaySessionID( ISteamInput* self, InputHandle_t inputHandle );
S_API uint16 SteamAPI_ISteamInput_GetSessionInputConfigurationSettings( ISteamInput* self );
+S_API void SteamAPI_ISteamInput_SetDualSenseTriggerEffect( ISteamInput* self, InputHandle_t inputHandle, const ScePadTriggerEffectParam * pParam );
// ISteamController
@@ -751,14 +753,14 @@ S_API bool SteamAPI_ISteamController_GetControllerBindingRevision( ISteamControl // ISteamUGC
// A versioned accessor is exported by the library
-S_API ISteamUGC *SteamAPI_SteamUGC_v016();
+S_API ISteamUGC *SteamAPI_SteamUGC_v017();
// Inline, unversioned accessor to get the current version. Essentially the same as SteamUGC(), but using this ensures that you are using a matching library.
-inline ISteamUGC *SteamAPI_SteamUGC() { return SteamAPI_SteamUGC_v016(); }
+inline ISteamUGC *SteamAPI_SteamUGC() { return SteamAPI_SteamUGC_v017(); }
// A versioned accessor is exported by the library
-S_API ISteamUGC *SteamAPI_SteamGameServerUGC_v016();
+S_API ISteamUGC *SteamAPI_SteamGameServerUGC_v017();
// Inline, unversioned accessor to get the current version. Essentially the same as SteamGameServerUGC(), but using this ensures that you are using a matching library.
-inline ISteamUGC *SteamAPI_SteamGameServerUGC() { return SteamAPI_SteamGameServerUGC_v016(); }
+inline ISteamUGC *SteamAPI_SteamGameServerUGC() { return SteamAPI_SteamGameServerUGC_v017(); }
S_API UGCQueryHandle_t SteamAPI_ISteamUGC_CreateQueryUserUGCRequest( ISteamUGC* self, AccountID_t unAccountID, EUserUGCList eListType, EUGCMatchingUGCType eMatchingUGCType, EUserUGCListSortOrder eSortOrder, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage );
S_API UGCQueryHandle_t SteamAPI_ISteamUGC_CreateQueryAllUGCRequestPage( ISteamUGC* self, EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, uint32 unPage );
S_API UGCQueryHandle_t SteamAPI_ISteamUGC_CreateQueryAllUGCRequestCursor( ISteamUGC* self, EUGCQuery eQueryType, EUGCMatchingUGCType eMatchingeMatchingUGCTypeFileType, AppId_t nCreatorAppID, AppId_t nConsumerAppID, const char * pchCursor );
@@ -777,6 +779,7 @@ S_API bool SteamAPI_ISteamUGC_GetQueryUGCAdditionalPreview( ISteamUGC* self, UGC S_API uint32 SteamAPI_ISteamUGC_GetQueryUGCNumKeyValueTags( ISteamUGC* self, UGCQueryHandle_t handle, uint32 index );
S_API bool SteamAPI_ISteamUGC_GetQueryUGCKeyValueTag( ISteamUGC* self, UGCQueryHandle_t handle, uint32 index, uint32 keyValueTagIndex, char * pchKey, uint32 cchKeySize, char * pchValue, uint32 cchValueSize );
S_API bool SteamAPI_ISteamUGC_GetQueryFirstUGCKeyValueTag( ISteamUGC* self, UGCQueryHandle_t handle, uint32 index, const char * pchKey, char * pchValue, uint32 cchValueSize );
+S_API uint32 SteamAPI_ISteamUGC_GetQueryUGCContentDescriptors( ISteamUGC* self, UGCQueryHandle_t handle, uint32 index, EUGCContentDescriptorID * pvecDescriptors, uint32 cMaxEntries );
S_API bool SteamAPI_ISteamUGC_ReleaseQueryUGCRequest( ISteamUGC* self, UGCQueryHandle_t handle );
S_API bool SteamAPI_ISteamUGC_AddRequiredTag( ISteamUGC* self, UGCQueryHandle_t handle, const char * pTagName );
S_API bool SteamAPI_ISteamUGC_AddRequiredTagGroup( ISteamUGC* self, UGCQueryHandle_t handle, const SteamParamStringArray_t * pTagGroups );
@@ -818,6 +821,8 @@ S_API bool SteamAPI_ISteamUGC_AddItemPreviewVideo( ISteamUGC* self, UGCUpdateHan S_API bool SteamAPI_ISteamUGC_UpdateItemPreviewFile( ISteamUGC* self, UGCUpdateHandle_t handle, uint32 index, const char * pszPreviewFile );
S_API bool SteamAPI_ISteamUGC_UpdateItemPreviewVideo( ISteamUGC* self, UGCUpdateHandle_t handle, uint32 index, const char * pszVideoID );
S_API bool SteamAPI_ISteamUGC_RemoveItemPreview( ISteamUGC* self, UGCUpdateHandle_t handle, uint32 index );
+S_API bool SteamAPI_ISteamUGC_AddContentDescriptor( ISteamUGC* self, UGCUpdateHandle_t handle, EUGCContentDescriptorID descid );
+S_API bool SteamAPI_ISteamUGC_RemoveContentDescriptor( ISteamUGC* self, UGCUpdateHandle_t handle, EUGCContentDescriptorID descid );
S_API SteamAPICall_t SteamAPI_ISteamUGC_SubmitItemUpdate( ISteamUGC* self, UGCUpdateHandle_t handle, const char * pchChangeNote );
S_API EItemUpdateStatus SteamAPI_ISteamUGC_GetItemUpdateProgress( ISteamUGC* self, UGCUpdateHandle_t handle, uint64 * punBytesProcessed, uint64 * punBytesTotal );
S_API SteamAPICall_t SteamAPI_ISteamUGC_SetUserItemVote( ISteamUGC* self, PublishedFileId_t nPublishedFileID, bool bVoteUp );
@@ -1118,9 +1123,9 @@ S_API bool SteamAPI_ISteamNetworkingUtils_SteamNetworkingIdentity_ParseString( I // ISteamGameServer
// A versioned accessor is exported by the library
-S_API ISteamGameServer *SteamAPI_SteamGameServer_v014();
+S_API ISteamGameServer *SteamAPI_SteamGameServer_v015();
// Inline, unversioned accessor to get the current version. Essentially the same as SteamGameServer(), but using this ensures that you are using a matching library.
-inline ISteamGameServer *SteamAPI_SteamGameServer() { return SteamAPI_SteamGameServer_v014(); }
+inline ISteamGameServer *SteamAPI_SteamGameServer() { return SteamAPI_SteamGameServer_v015(); }
S_API void SteamAPI_ISteamGameServer_SetProduct( ISteamGameServer* self, const char * pszProduct );
S_API void SteamAPI_ISteamGameServer_SetGameDescription( ISteamGameServer* self, const char * pszGameDescription );
S_API void SteamAPI_ISteamGameServer_SetModDir( ISteamGameServer* self, const char * pszModDir );
@@ -1145,7 +1150,7 @@ S_API void SteamAPI_ISteamGameServer_SetGameTags( ISteamGameServer* self, const S_API void SteamAPI_ISteamGameServer_SetGameData( ISteamGameServer* self, const char * pchGameData );
S_API void SteamAPI_ISteamGameServer_SetRegion( ISteamGameServer* self, const char * pszRegion );
S_API void SteamAPI_ISteamGameServer_SetAdvertiseServerActive( ISteamGameServer* self, bool bActive );
-S_API HAuthTicket SteamAPI_ISteamGameServer_GetAuthSessionTicket( ISteamGameServer* self, void * pTicket, int cbMaxTicket, uint32 * pcbTicket );
+S_API HAuthTicket SteamAPI_ISteamGameServer_GetAuthSessionTicket( ISteamGameServer* self, void * pTicket, int cbMaxTicket, uint32 * pcbTicket, const SteamNetworkingIdentity * pSnid );
S_API EBeginAuthSessionResult SteamAPI_ISteamGameServer_BeginAuthSession( ISteamGameServer* self, const void * pAuthTicket, int cbAuthTicket, uint64_steamid steamID );
S_API void SteamAPI_ISteamGameServer_EndAuthSession( ISteamGameServer* self, uint64_steamid steamID );
S_API void SteamAPI_ISteamGameServer_CancelAuthTicket( ISteamGameServer* self, HAuthTicket hAuthTicket );
diff --git a/public/steam/steam_api_internal.h b/public/steam/steam_api_internal.h index ac005a86..86defb10 100644 --- a/public/steam/steam_api_internal.h +++ b/public/steam/steam_api_internal.h @@ -54,12 +54,6 @@ S_API void S_CALLTYPE SteamAPI_UnregisterCallback( class CCallbackBase *pCallbac S_API void S_CALLTYPE SteamAPI_RegisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall );
S_API void S_CALLTYPE SteamAPI_UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall );
-// disable this warning; this pattern need for steam callback registration
-#ifdef _MSVC_VER
-#pragma warning( push )
-#pragma warning( disable: 4355 ) // 'this' : used in base member initializer list
-#endif
-
#define _STEAM_CALLBACK_AUTO_HOOK( thisclass, func, param )
#define _STEAM_CALLBACK_HELPER( _1, _2, SELECTED, ... ) _STEAM_CALLBACK_##SELECTED
#define _STEAM_CALLBACK_SELECT( X, Y ) _STEAM_CALLBACK_HELPER X Y
@@ -244,6 +238,9 @@ class ISteamInput; class ISteamParties;
class ISteamRemotePlay;
+// Forward declare types
+struct SteamNetworkingIdentity;
+
//-----------------------------------------------------------------------------
// Purpose: Base values for callback identifiers, each callback must
// have a unique ID.
@@ -289,10 +286,6 @@ enum { k_iSteamRemotePlayCallbacks = 5700 }; enum { k_iSteamChatCallbacks = 5900 };
// NOTE: Internal "IClientXxx" callback IDs go in clientenums.h
-#ifdef _MSVC_VER
-#pragma warning( pop )
-#endif
-
// Macros used to annotate various Steamworks interfaces to generate the
// flat API
#ifdef API_GEN
diff --git a/public/steam/steamclientpublic.h b/public/steam/steamclientpublic.h index 876b6dbe..ee6b9783 100644 --- a/public/steam/steamclientpublic.h +++ b/public/steam/steamclientpublic.h @@ -79,7 +79,7 @@ enum EResult k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
k_EResultCannotUseOldPassword = 64, // The requested new password is not legal
k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid
- k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
+ k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent - partner site specific
k_EResultHardwareNotCapableOfIPT = 67, //
k_EResultIPTInitError = 68, //
k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
@@ -140,6 +140,7 @@ enum EResult k_EResultInsufficientBattery = 124, // user device doesn't have enough battery charge currently to complete the action
k_EResultChargerRequired = 125, // The operation requires a charger to be plugged in, which wasn't present
k_EResultCachedCredentialInvalid = 126, // Cached credential was invalid - user must reauthenticate
+ K_EResultPhoneNumberIsVOIP = 127, // The phone number provided is a Voice Over IP number
};
// Error codes for use with the voice functions
@@ -207,6 +208,7 @@ enum EAuthSessionResponse k_EAuthSessionResponseAuthTicketInvalidAlreadyUsed = 7, // This ticket has already been used, it is not valid.
k_EAuthSessionResponseAuthTicketInvalid = 8, // This ticket is not from a user instance currently connected to steam.
k_EAuthSessionResponsePublisherIssuedBan = 9, // The user is banned for this game. The ban came via the web api and not VAC
+ k_EAuthSessionResponseAuthTicketNetworkIdentityFailure = 10, // The network identity in the ticket does not match the server authenticating the ticket
};
// results from UserHasLicenseForApp
@@ -309,6 +311,7 @@ enum EChatSteamIDInstanceFlags //-----------------------------------------------------------------------------
enum ENotificationPosition
{
+ k_EPositionInvalid = -1,
k_EPositionTopLeft = 0,
k_EPositionTopRight = 1,
k_EPositionBottomLeft = 2,
@@ -907,6 +910,14 @@ class CGameID {
public:
+ enum EGameIDType
+ {
+ k_EGameIDTypeApp = 0,
+ k_EGameIDTypeGameMod = 1,
+ k_EGameIDTypeShortcut = 2,
+ k_EGameIDTypeP2P = 3,
+ };
+
CGameID()
{
m_gameID.m_nType = k_EGameIDTypeApp;
@@ -937,12 +948,12 @@ public: m_gameID.m_nAppID = nAppID;
}
- CGameID( uint32 nAppID, uint32 nModID )
+ // Not validating anything .. use IsValid()
+ explicit CGameID( uint32 nAppID, uint32 nModID, CGameID::EGameIDType nType )
{
- m_ulGameID = 0;
m_gameID.m_nAppID = nAppID;
m_gameID.m_nModID = nModID;
- m_gameID.m_nType = k_EGameIDTypeGameMod;
+ m_gameID.m_nType = nType;
}
CGameID( const CGameID &that )
@@ -1001,10 +1012,14 @@ public: return m_gameID.m_nModID;
}
- uint32 AppID() const
+#if !defined(VALVE_SHORTCUT_DEBUG)
+ uint32 AppID( bool = false ) const
{
return m_gameID.m_nAppID;
}
+#else
+ uint32 AppID( bool bShortcutOK = false ) const;
+#endif
bool operator == ( const CGameID &rhs ) const
{
@@ -1030,13 +1045,15 @@ public: return m_gameID.m_nAppID != k_uAppIdInvalid;
case k_EGameIDTypeGameMod:
- return m_gameID.m_nAppID != k_uAppIdInvalid && m_gameID.m_nModID & 0x80000000;
+ return m_gameID.m_nAppID != k_uAppIdInvalid && (m_gameID.m_nModID & 0x80000000);
case k_EGameIDTypeShortcut:
- return (m_gameID.m_nModID & 0x80000000) != 0;
+ return m_gameID.m_nAppID == k_uAppIdInvalid
+ && (m_gameID.m_nModID & 0x80000000)
+ && m_gameID.m_nModID >= (5000 | 0x80000000); // k_unMaxExpectedLocalAppId - shortcuts are pushed beyond that range
case k_EGameIDTypeP2P:
- return m_gameID.m_nAppID == k_uAppIdInvalid && m_gameID.m_nModID & 0x80000000;
+ return m_gameID.m_nAppID == k_uAppIdInvalid && (m_gameID.m_nModID & 0x80000000);
default:
return false;
@@ -1053,14 +1070,6 @@ public: // Internal stuff. Use the accessors above if possible
//
- enum EGameIDType
- {
- k_EGameIDTypeApp = 0,
- k_EGameIDTypeGameMod = 1,
- k_EGameIDTypeShortcut = 2,
- k_EGameIDTypeP2P = 3,
- };
-
struct GameID_t
{
#ifdef VALVE_BIG_ENDIAN
@@ -1079,6 +1088,8 @@ public: uint64 m_ulGameID;
GameID_t m_gameID;
};
+
+ friend CGameID GameIDFromAppAndModPath( uint32 nAppID, const char *pchModPath );
};
#pragma pack( pop )
@@ -1134,7 +1145,7 @@ enum ESteamIPv6ConnectivityState // Define compile time assert macros to let us validate the structure sizes.
#define VALVE_COMPILE_TIME_ASSERT( pred ) typedef char compile_time_assert_type[(pred) ? 1 : -1];
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
// The 32-bit version of gcc has the alignment requirement for uint64 and double set to
// 4 meaning that even with #pragma pack(8) these types will only be four-byte aligned.
// The 64-bit version of gcc has the alignment requirement for these types set to
diff --git a/public/steam/steamhttpenums.h b/public/steam/steamhttpenums.h index ff8551ef..ecb21ed7 100644 --- a/public/steam/steamhttpenums.h +++ b/public/steam/steamhttpenums.h @@ -62,6 +62,7 @@ enum EHTTPStatusCode k_EHTTPStatusCode305UseProxy = 305,
//k_EHTTPStatusCode306Unused = 306, (used in old HTTP spec, now unused in 1.1)
k_EHTTPStatusCode307TemporaryRedirect = 307,
+ k_EHTTPStatusCode308PermanentRedirect = 308,
// Error codes
k_EHTTPStatusCode400BadRequest = 400,
diff --git a/public/steam/steamnetworkingtypes.h b/public/steam/steamnetworkingtypes.h index ebcbdfd3..a1b93924 100644 --- a/public/steam/steamnetworkingtypes.h +++ b/public/steam/steamnetworkingtypes.h @@ -1699,7 +1699,7 @@ inline SteamNetworkingPOPID CalculateSteamNetworkingPOPIDFromString( const char //
// There is also extra paranoia to make sure the bytes are not treated as signed.
SteamNetworkingPOPID result = (uint32)(uint8)pszCode[0] << 16U;
- if ( pszCode[1] )
+ if ( result && pszCode[1] )
{
result |= ( (uint32)(uint8)pszCode[1] << 8U );
if ( pszCode[2] )
diff --git a/public/steam/steamtypes.h b/public/steam/steamtypes.h index 966aa122..49653c4a 100644 --- a/public/steam/steamtypes.h +++ b/public/steam/steamtypes.h @@ -1,27 +1,20 @@ -//========= Copyright © 1996-2008, Valve LLC, All rights reserved. ============
-//
-// Purpose:
-//
-//=============================================================================
+//========= Copyright 1996-2022, Valve LLC, All rights reserved. ============
#ifndef STEAMTYPES_H
#define STEAMTYPES_H
-#ifdef _WIN32
-#pragma once
-#endif
#define S_CALLTYPE __cdecl
+// WARNING: __cdecl is potentially #defined away in steam_api_common.h
// Steam-specific types. Defined here so this header file can be included in other code bases.
#ifndef WCHARTYPES_H
typedef unsigned char uint8;
#endif
-#if defined( __GNUC__ ) && !defined(_WIN32) && !defined(POSIX)
+#ifdef __GNUC__
#if __GNUC__ < 4
#error "Steamworks requires GCC 4.X (4.2 or 4.4 have been tested)"
#endif
- #define POSIX 1
#endif
#if defined(__LP64__) || defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) || defined(__s390x__)
diff --git a/release_checklist.md b/release_checklist.md index f5c02b3b..22a17169 100644 --- a/release_checklist.md +++ b/release_checklist.md @@ -24,7 +24,7 @@ 1. `git push` from main development machine, check the branch -2. Start the `endeavouros64` VirtualBox virtual machine +2. Start the `ubuntu2204lts` VirtualBox virtual machine 3. Go into `SSVOpenHexagon` directory @@ -36,6 +36,10 @@ 7. The Linux client build will automatically be copied to the main development machine's drive +8. Run `/c/OHWorkspace/steamworks/sdk/tools/SteamPipeGUI.exe` and upload to Steam + + - Use depot ID `1358092`, build path `C:\OHWorkspace\SSVOpenHexagon\_PREPARED_RELEASE_LINUX` +- ## Windows Client 1. Run `SSVOpenHexagon/buildrel/make_release_client_win10_msys.sh` @@ -54,6 +58,8 @@ 4. Run `/c/OHWorkspace/steamworks/sdk/tools/SteamPipeGUI.exe` and upload to Steam + - Use depot ID `1358091`, build path `C:\OHWorkspace\SSVOpenHexagon\_PREPARED_RELEASE` + 5. For non-betas, go to <https://partner.steamgames.com/apps/builds/1358090>, log in with build account (not personal one), and put the latest build live ## Lua Reference @@ -86,4 +92,14 @@ 2. Put level validator strings in `Config.cpp`. -3. TODO +3. Upload workshop folders from `C:\Program Files (x86)\Steam\steamapps\workshop\content\1358090` to the server's `Packs` folder. + +4. Delete the server's `config.json`. + +5. Restart the server and test. + +## Other + +- Ubuntu machine packages: + + - sudo apt-get install build-essential clang++-12 g++ g++-12 git libfreetype6-dev libgl1-mesa-dev libglew-dev libjpeg-dev libopenal-dev libpthread-stubs0-dev libsndfile1-dev libx11-dev libxrandr-dev lld ninja-build xorg-dev xserver-xorg-dev libudev-dev vim steam diff --git a/src/SSVOpenHexagon/Components/CPlayer.cpp b/src/SSVOpenHexagon/Components/CPlayer.cpp index efded9fb..e2409dde 100644 --- a/src/SSVOpenHexagon/Components/CPlayer.cpp +++ b/src/SSVOpenHexagon/Components/CPlayer.cpp @@ -353,7 +353,7 @@ template <typename Wall> const std::array<sf::Vector2f, 4> collisionPolygon{ wVertexes[i], wOldVertexes[i], wOldVertexes[j], wVertexes[j]}; - if(Utils::pointInPolygon(collisionPolygon, _lastPos.x, _lastPos.y)) + if(Utils::pointInPolygon<4>(collisionPolygon, _lastPos.x, _lastPos.y)) { // For a side to be an effective source of push it must have // intersected the player's positions circle both now and the diff --git a/src/SSVOpenHexagon/Components/CWall.cpp b/src/SSVOpenHexagon/Components/CWall.cpp index 88b5bd08..8d0af0e7 100644 --- a/src/SSVOpenHexagon/Components/CWall.cpp +++ b/src/SSVOpenHexagon/Components/CWall.cpp @@ -5,6 +5,8 @@ #include "SSVOpenHexagon/Components/CWall.hpp" #include "SSVOpenHexagon/Utils/Color.hpp" +#include <SFML/System/Vector2.hpp> + namespace hg { CWall::CWall(const unsigned int sides, const float wallAngleLeft, @@ -14,15 +16,25 @@ CWall::CWall(const unsigned int sides, const float wallAngleLeft, const SpeedData& curve, const float hueMod) : _speed{speed}, _curve{curve}, _hueMod{hueMod}, _killed{false} { - const float div{ssvu::tau / sides * 0.5f}; - const float angle{div * 2.f * side}; - - _vertexPositions[0] = ssvs::getOrbitRad(centerPos, angle - div, distance); - _vertexPositions[1] = ssvs::getOrbitRad(centerPos, angle + div, distance); - _vertexPositions[2] = ssvs::getOrbitRad(centerPos, - angle + div + wallAngleLeft, distance + thickness + wallSkewLeft); - _vertexPositions[3] = ssvs::getOrbitRad(centerPos, - angle - div + wallAngleRight, distance + thickness + wallSkewRight); + const float div{ssvu::tau / static_cast<float>(sides) * 0.5f}; + const float angle{div * 2.f * static_cast<float>(side)}; + + const float angleN = angle - div; + const float angleP = angle + div; + + const auto vecFromRad = [](const float rad, const float dist) { + return sf::Vector2f{dist * std::cos(rad), dist * std::sin(rad)}; + }; + + _vertexPositions[0] = centerPos + vecFromRad(angleN, distance); + + _vertexPositions[1] = centerPos + vecFromRad(angleP, distance); + + _vertexPositions[2] = centerPos + vecFromRad(angleP + wallAngleLeft, + distance + thickness + wallSkewLeft); + + _vertexPositions[3] = centerPos + vecFromRad(angleN + wallAngleRight, + distance + thickness + wallSkewRight); } void CWall::draw(sf::Color color, Utils::FastVertexVectorTris& wallQuads) @@ -74,7 +86,7 @@ void CWall::moveTowardsCenter(const float wallSpawnDist, const float radius, ++pointsOutOfBounds; } - ssvs::moveTowards(vp, centerPos, _speed._speed * 5.f * ft); + vp += (centerPos - vp).normalized() * _speed._speed * 5.f * ft; } if(pointsOnCenter == 4 || pointsOutOfBounds == 4) @@ -85,9 +97,13 @@ void CWall::moveTowardsCenter(const float wallSpawnDist, const float radius, void CWall::moveCurve(const sf::Vector2f& centerPos, const ssvu::FT ft) { + const float rad = getCurveRadians(ft); + const float radSin = std::sin(rad); + const float radCos = std::cos(rad); + for(sf::Vector2f& vp : _vertexPositions) { - moveVertexAlongCurve(vp, centerPos, ft); + moveVertexAlongCurveImpl(vp, centerPos, radSin, radCos); } } diff --git a/src/SSVOpenHexagon/Core/BindControl.cpp b/src/SSVOpenHexagon/Core/BindControl.cpp index d3092d14..90c4af07 100644 --- a/src/SSVOpenHexagon/Core/BindControl.cpp +++ b/src/SSVOpenHexagon/Core/BindControl.cpp @@ -166,7 +166,7 @@ void JoystickBindControl::newJoystickBind(const unsigned int joy) [[nodiscard]] std::string JoystickBindControl::getName() const { std::string bindName = - Config::getJoystickBindNames(static_cast<Joystick::Jid>(ID)); + Config::getJoystickBindName(static_cast<Joystick::Jid>(ID)); if(waitingForBind) { diff --git a/src/SSVOpenHexagon/Core/CCustomWallManager.cpp b/src/SSVOpenHexagon/Core/CCustomWallManager.cpp index a3c38792..33faf39a 100644 --- a/src/SSVOpenHexagon/Core/CCustomWallManager.cpp +++ b/src/SSVOpenHexagon/Core/CCustomWallManager.cpp @@ -9,7 +9,6 @@ #include <SSVUtils/Core/Log/Log.hpp> #include <SSVUtils/Core/Utils/Containers.hpp> -#include <SSVUtils/Core/Common/LikelyUnlikely.hpp> namespace hg { @@ -24,7 +23,7 @@ namespace hg { [[nodiscard]] bool CCustomWallManager::checkValidHandle( const CCustomWallHandle h, const char* msg) { - if(SSVU_UNLIKELY(_handleAvailable[h])) + if(_handleAvailable[h]) [[unlikely]] { ssvu::lo("CustomWallManager") << "Attempted to " << msg << " of invalid custom wall " << h @@ -41,7 +40,7 @@ namespace hg { [[nodiscard]] bool CCustomWallManager::checkValidVertexIdx( const CCustomWallHandle h, const int vertexIdx, const char* msg) { - if(SSVU_UNLIKELY(vertexIdx < 0 || vertexIdx > 3)) + if(vertexIdx < 0 || vertexIdx > 3) [[unlikely]] { ssvu::lo("CustomWallManager") << "Invalid vertex index " << vertexIdx << " for custom wall " << h @@ -62,7 +61,7 @@ namespace hg { [[nodiscard]] CCustomWallHandle CCustomWallManager::create( void (*fAfterCreate)(CCustomWall&)) { - if(SSVU_UNLIKELY(_freeHandles.empty())) + if(_freeHandles.empty()) [[unlikely]] { const std::size_t reserveSize = 32 + _nextFreeHandle * 2; const std::size_t maxHandleIndex = _nextFreeHandle + reserveSize; @@ -109,7 +108,7 @@ void CCustomWallManager::destroyUnchecked(const CCustomWallHandle cwHandle) void CCustomWallManager::destroy(const CCustomWallHandle cwHandle) { - if(SSVU_UNLIKELY(_handleAvailable[cwHandle])) + if(_handleAvailable[cwHandle]) [[unlikely]] { ssvu::lo("CustomWallManager") << "Attempted to destroy invalid wall " << cwHandle << '\n'; @@ -178,7 +177,7 @@ void CCustomWallManager::setDeadly( void CCustomWallManager::setKillingSide( const CCustomWallHandle cwHandle, const std::uint8_t side) { - if(SSVU_UNLIKELY(side > 3u)) + if(side > 3u) [[unlikely]] { ssvu::lo("CustomWallManager") << "Attempted to set killing side with invalid value " << side @@ -319,7 +318,7 @@ void CCustomWallManager::clear() void CCustomWallManager::draw(Utils::FastVertexVectorTris& wallQuads) { - for(CCustomWallHandle h = 0; h < (int)_customWalls.size(); ++h) + for(CCustomWallHandle h = 0; h < static_cast<int>(_customWalls.size()); ++h) { if(!_handleAvailable[h]) { diff --git a/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp b/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp index 6f43e551..8a2bafb4 100644 --- a/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp +++ b/src/SSVOpenHexagon/Core/CustomTimelineManager.cpp @@ -2,8 +2,6 @@ // License: Academic Free License ("AFL") v. 3.0 // AFL License page: https://opensource.org/licenses/AFL-3.0 -#pragma once - #include "SSVOpenHexagon/Core/CustomTimelineManager.hpp" #include "SSVOpenHexagon/Core/CustomTimeline.hpp" diff --git a/src/SSVOpenHexagon/Core/HGGraphics.cpp b/src/SSVOpenHexagon/Core/HGGraphics.cpp index cb0dd9c9..5835a2e3 100644 --- a/src/SSVOpenHexagon/Core/HGGraphics.cpp +++ b/src/SSVOpenHexagon/Core/HGGraphics.cpp @@ -51,7 +51,7 @@ void HexagonGame::render( void HexagonGame::draw() { - if(window == nullptr) + if(window == nullptr || Config::getDisableGameRendering()) { return; } diff --git a/src/SSVOpenHexagon/Core/HGScripting.cpp b/src/SSVOpenHexagon/Core/HGScripting.cpp index 7e05d4eb..974b673c 100644 --- a/src/SSVOpenHexagon/Core/HGScripting.cpp +++ b/src/SSVOpenHexagon/Core/HGScripting.cpp @@ -13,6 +13,8 @@ #include "SSVOpenHexagon/Core/LuaScripting.hpp" #include "SSVOpenHexagon/Core/Steam.hpp" +#include "SSVOpenHexagon/Data/LevelData.hpp" + #include "SSVOpenHexagon/Global/Assets.hpp" #include "SSVOpenHexagon/Global/Audio.hpp" #include "SSVOpenHexagon/Global/Config.hpp" @@ -1120,7 +1122,16 @@ void HexagonGame::initLua() void HexagonGame::runLuaFile(const std::string& mFileName) try { - Utils::runLuaFile(lua, mFileName); + const bool headless = window == nullptr; + + if(headless || Config::getUseLuaFileCache()) + { + Utils::runLuaFileCached(assets, lua, mFileName); + } + else + { + Utils::runLuaFile(lua, mFileName); + } } catch(...) { @@ -1136,7 +1147,7 @@ void HexagonGame::initLuaAndPrintDocs() LuaScripting::printDocs(); } -void HexagonGame::luaExceptionLippincottHandler(const std::string& mName) +void HexagonGame::luaExceptionLippincottHandler(std::string_view mName) try { throw; diff --git a/src/SSVOpenHexagon/Core/HGUpdate.cpp b/src/SSVOpenHexagon/Core/HGUpdate.cpp index 866bad89..bbd1adc8 100644 --- a/src/SSVOpenHexagon/Core/HGUpdate.cpp +++ b/src/SSVOpenHexagon/Core/HGUpdate.cpp @@ -6,6 +6,8 @@ #include "SSVOpenHexagon/Components/CWall.hpp" +#include "SSVOpenHexagon/Data/LevelData.hpp" + #include "SSVOpenHexagon/Global/Assert.hpp" #include "SSVOpenHexagon/Global/Assets.hpp" #include "SSVOpenHexagon/Global/Audio.hpp" @@ -127,27 +129,32 @@ void HexagonGame::update(ssvu::FT mFT, const float timescale) // ------------------------------------------------------------------------ // Update Discord and Steam "rich presence". // Discord "rich presence" is also updated in `HexagonGame::start`. - std::string nameStr = levelData->name; - nameFormat(nameStr); - const std::string diffStr = diffFormat(difficultyMult); - const std::string timeStr = timeFormat(status.getTimeSeconds()); + if(window != nullptr) + { + std::string nameStr = levelData->name; + nameFormat(nameStr); + + const std::string diffStr = diffFormat(difficultyMult); + const std::string timeStr = timeFormat(status.getTimeSeconds()); - constexpr float DELAY_TO_UPDATE = 5.f; // X seconds - timeUntilRichPresenceUpdate -= ssvu::getFTToSeconds(mFT); + constexpr float DELAY_TO_UPDATE = 5.f; // X seconds + timeUntilRichPresenceUpdate -= ssvu::getFTToSeconds(mFT); - if(timeUntilRichPresenceUpdate <= 0.f) - { - if(steamManager != nullptr) + if(timeUntilRichPresenceUpdate <= 0.f) { - steamManager->set_rich_presence_in_game(nameStr, diffStr, timeStr); + if(steamManager != nullptr) + { + steamManager->set_rich_presence_in_game( + nameStr, diffStr, timeStr); + } + + timeUntilRichPresenceUpdate = DELAY_TO_UPDATE; } - timeUntilRichPresenceUpdate = DELAY_TO_UPDATE; + updateRichPresenceCallbacks(); } - updateRichPresenceCallbacks(); - // ------------------------------------------------------------------------ if(mustStart) @@ -516,12 +523,14 @@ void HexagonGame::start() nameStr + " [x" + diffStr + "]", packStr); } + const std::string& validator = levelData->getValidator(difficultyMult); + if(hexagonClient != nullptr && hexagonClient->getState() == HexagonClient::State::LoggedIn_Ready && - Config::getOfficial()) + Config::getOfficial() && !levelData->unscored && + hexagonClient->isLevelSupportedByServer(validator)) { - hexagonClient->trySendStartedGame( - levelData->getValidator(difficultyMult)); + hexagonClient->trySendStartedGame(validator); } } else @@ -550,6 +559,11 @@ static void setInputImplIfFalse(bool& var, const bool x) void HexagonGame::updateInput_UpdateJoystickControls() { + if(window == nullptr) + { + return; + } + Joystick::update(Config::getJoystickDeadzone()); setInputImplIfFalse(inputImplCCW, Joystick::pressed(Joystick::Jdir::Left)); diff --git a/src/SSVOpenHexagon/Core/HexagonClient.cpp b/src/SSVOpenHexagon/Core/HexagonClient.cpp index f9665963..2741c194 100644 --- a/src/SSVOpenHexagon/Core/HexagonClient.cpp +++ b/src/SSVOpenHexagon/Core/HexagonClient.cpp @@ -259,7 +259,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending registration request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPRegister{ .steamId = steamId, // .name = name, // @@ -273,7 +273,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending login request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPLogin{ .steamId = steamId, // .name = name, // @@ -293,7 +293,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending delete account request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPDeleteAccount{ .steamId = steamId, // .passwordHash = passwordHash // @@ -306,7 +306,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending top scores request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPRequestTopScores{ .loginToken = loginToken, // .levelValidator = levelValidator // @@ -319,7 +319,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending own score request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPRequestOwnScore{ .loginToken = loginToken, // .levelValidator = levelValidator // @@ -333,7 +333,7 @@ template <typename T> SSVOH_CLOG_VERBOSE << "Sending top scores and own score request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPRequestTopScoresAndOwnScore{ .loginToken = loginToken, // .levelValidator = levelValidator // @@ -346,7 +346,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending started game packet to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPStartedGame{ .loginToken = loginToken, // .levelValidator = levelValidator // @@ -361,7 +361,7 @@ template <typename T> SSVOH_CLOG_VERBOSE << "Sending compressed replay for level validator '" << levelValidator << "' to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPCompressedReplay{ .loginToken = loginToken, // .compressedReplayFile = compressedReplayFile // @@ -374,7 +374,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending status request to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPRequestServerStatus{ .loginToken = loginToken, // } // @@ -385,7 +385,7 @@ template <typename T> { SSVOH_CLOG_VERBOSE << "Sending ready to server...\n"; - return sendEncrypted( // + return sendEncrypted( // CTSPReady{ .loginToken = loginToken, // } // diff --git a/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp b/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp index 53fc5231..19e8a800 100644 --- a/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp +++ b/src/SSVOpenHexagon/Core/HexagonDialogBox.cpp @@ -9,6 +9,8 @@ #include <SSVStart/Utils/SFML.hpp> #include <SSVStart/GameSystem/GameWindow.hpp> +#include <SFML/Graphics/Font.hpp> + #include <algorithm> #include <string> #include <tuple> diff --git a/src/SSVOpenHexagon/Core/HexagonGame.cpp b/src/SSVOpenHexagon/Core/HexagonGame.cpp index 57ddfcc4..5b5241bc 100644 --- a/src/SSVOpenHexagon/Core/HexagonGame.cpp +++ b/src/SSVOpenHexagon/Core/HexagonGame.cpp @@ -6,6 +6,12 @@ #include "SSVOpenHexagon/Components/CWall.hpp" +#include "SSVOpenHexagon/Data/LevelData.hpp" +#include "SSVOpenHexagon/Data/StyleData.hpp" +#include "SSVOpenHexagon/Data/PackData.hpp" +#include "SSVOpenHexagon/Data/PackInfo.hpp" +#include "SSVOpenHexagon/Data/ProfileData.hpp" + #include "SSVOpenHexagon/Global/Assert.hpp" #include "SSVOpenHexagon/Global/Assets.hpp" #include "SSVOpenHexagon/Global/Audio.hpp" @@ -384,7 +390,7 @@ HexagonGame::HexagonGame(Steam::steam_manager* mSteamManager, notInConsole( [this] { - if((deathInputIgnore <= 0.f && status.hasDied) || inReplay()) + if(deathInputIgnore <= 0.f && status.hasDied) { status.mustStateChange = StateChange::MustRestart; } @@ -394,7 +400,7 @@ HexagonGame::HexagonGame(Steam::steam_manager* mSteamManager, notInConsole( [this] { - if(deathInputIgnore <= 0.f && status.hasDied) + if((deathInputIgnore <= 0.f && status.hasDied) || inReplay()) { status.mustStateChange = StateChange::MustReplay; } @@ -748,62 +754,68 @@ void HexagonGame::newGame(const std::string& mPackId, const std::string& mId, status.beatPulseDelay += levelStatus.beatPulseInitialDelay; timeUntilRichPresenceUpdate = -1.f; // immediate update - // Store the keys/buttons to be pressed to replay and restart after you - // die. - using Tid = Config::Tid; - status.restartInput = Config::getKeyboardBindNames(Tid::Restart); - status.replayInput = Config::getKeyboardBindNames(Tid::Replay); - - // Format strings to only show the first key to avoid extremely long - // messages - int commaPos = status.restartInput.find(','); - if(commaPos > 0) - { - status.restartInput.erase(commaPos); - } - commaPos = status.replayInput.find(','); - if(commaPos > 0) + // Prepare text for input hints on restart/replay + if(window != nullptr) { - status.replayInput.erase(commaPos); - } + // Store the keys/buttons to be pressed to replay and restart after you + // die. + using Tid = Config::Tid; + status.restartInput = Config::getKeyboardBindNames(Tid::Restart); + status.replayInput = Config::getKeyboardBindNames(Tid::Replay); - // Add joystick buttons if any and finalize message - std::string joystickButton = - Config::getJoystickBindNames(Joystick::Jid::Restart); - if(!status.restartInput.empty()) - { - if(!joystickButton.empty()) + // Format strings to only show the first key to avoid extremely long + // messages + int commaPos = status.restartInput.find(','); + if(commaPos > 0) { - status.restartInput += " OR JOYSTICK " + joystickButton; + status.restartInput.erase(commaPos); } - status.restartInput = "PRESS " + status.restartInput + " TO RESTART\n"; - } - else if(!joystickButton.empty()) - { - status.restartInput = - "PRESS JOYSTICK " + joystickButton + " TO RESTART\n"; - } - else - { - status.restartInput = "NO RESTART BUTTON SET\n"; - } - joystickButton = Config::getJoystickBindNames(Joystick::Jid::Replay); - if(!status.replayInput.empty()) - { - if(!joystickButton.empty()) + commaPos = status.replayInput.find(','); + if(commaPos > 0) { - status.replayInput += " OR JOYSTICK " + joystickButton; + status.replayInput.erase(commaPos); + } + + // Add joystick buttons if any and finalize message + std::string joystickButton = + Config::getJoystickBindName(Joystick::Jid::Restart); + if(!status.restartInput.empty()) + { + if(!joystickButton.empty()) + { + status.restartInput += " OR JOYSTICK " + joystickButton; + } + status.restartInput = + "PRESS " + status.restartInput + " TO RESTART\n"; + } + else if(!joystickButton.empty()) + { + status.restartInput = + "PRESS JOYSTICK " + joystickButton + " TO RESTART\n"; + } + else + { + status.restartInput = "NO RESTART BUTTON SET\n"; + } + + joystickButton = Config::getJoystickBindName(Joystick::Jid::Replay); + if(!status.replayInput.empty()) + { + if(!joystickButton.empty()) + { + status.replayInput += " OR JOYSTICK " + joystickButton; + } + status.replayInput = "PRESS " + status.replayInput + " TO REPLAY\n"; + } + else if(!joystickButton.empty()) + { + status.replayInput = + "PRESS JOYSTICK " + joystickButton + " TO REPLAY\n"; + } + else + { + status.replayInput = "NO REPLAY BUTTON SET\n"; } - status.replayInput = "PRESS " + status.replayInput + " TO REPLAY\n"; - } - else if(!joystickButton.empty()) - { - status.replayInput = - "PRESS JOYSTICK " + joystickButton + " TO REPLAY\n"; - } - else - { - status.replayInput = "NO REPLAY BUTTON SET\n"; } } diff --git a/src/SSVOpenHexagon/Core/HexagonServer.cpp b/src/SSVOpenHexagon/Core/HexagonServer.cpp index 73b102f4..112a15c5 100644 --- a/src/SSVOpenHexagon/Core/HexagonServer.cpp +++ b/src/SSVOpenHexagon/Core/HexagonServer.cpp @@ -4,6 +4,8 @@ #include "SSVOpenHexagon/Core/HexagonServer.hpp" +#include "SSVOpenHexagon/Data/LevelData.hpp" + #include "SSVOpenHexagon/Global/Assert.hpp" #include "SSVOpenHexagon/Global/Assets.hpp" #include "SSVOpenHexagon/Global/Config.hpp" @@ -176,7 +178,7 @@ template <typename T> [[nodiscard]] bool HexagonServer::sendLoginSuccess(ConnectedClient& c, const std::uint64_t loginToken, const std::string& loginName) { - return sendEncrypted(c, // + return sendEncrypted(c, // STCPLoginSuccess{ .loginToken = static_cast<std::uint64_t>(loginToken), // .loginName = loginName // @@ -215,7 +217,7 @@ template <typename T> const std::string& levelValidator, const std::vector<Database::ProcessedScore>& scores) { - return sendEncrypted(c, // + return sendEncrypted(c, // STCPTopScores{ .levelValidator = levelValidator, // .scores = scores // @@ -226,7 +228,7 @@ template <typename T> [[nodiscard]] bool HexagonServer::sendOwnScore(ConnectedClient& c, const std::string& levelValidator, const Database::ProcessedScore& score) { - return sendEncrypted(c, // + return sendEncrypted(c, // STCPOwnScore{ .levelValidator = levelValidator, // .score = score // @@ -239,7 +241,7 @@ template <typename T> const std::vector<Database::ProcessedScore>& scores, const std::optional<Database::ProcessedScore>& ownScore) { - return sendEncrypted(c, // + return sendEncrypted(c, // STCPTopScoresAndOwnScore{ .levelValidator = levelValidator, // .scores = scores, // @@ -252,7 +254,7 @@ template <typename T> const ProtocolVersion& protocolVersion, const GameVersion& gameVersion, const std::vector<std::string>& supportedLevelValidators) { - return sendEncrypted(c, // + return sendEncrypted(c, // STCPServerStatus{ .protocolVersion = protocolVersion, // .gameVersion = gameVersion, // diff --git a/src/SSVOpenHexagon/Core/Joystick.cpp b/src/SSVOpenHexagon/Core/Joystick.cpp index fae0a4ce..45e69c43 100644 --- a/src/SSVOpenHexagon/Core/Joystick.cpp +++ b/src/SSVOpenHexagon/Core/Joystick.cpp @@ -128,8 +128,6 @@ enum class AxisDir : int void update(const float deadzone) { - sf::Joystick::update(); - constexpr unsigned int joyId = 0; auto& s = getJoystickState(); diff --git a/src/SSVOpenHexagon/Core/LuaScripting.cpp b/src/SSVOpenHexagon/Core/LuaScripting.cpp index e3985240..94201365 100644 --- a/src/SSVOpenHexagon/Core/LuaScripting.cpp +++ b/src/SSVOpenHexagon/Core/LuaScripting.cpp @@ -1130,20 +1130,21 @@ static void initStyleControl(Lua::LuaContext& lua, StyleData& styleData) "Sets the rotation offset of the background panels to `$0` degrees."); addLuaFn(lua, "s_setCapColorMain", // - [&styleData] { styleData.setCapColor(CapColorMode::Main{}); }) + [&styleData] { styleData.setCapColor(CapColor{CapColorMode::Main{}}); }) .doc( "Set the color of the center polygon to match the main style " "color."); addLuaFn(lua, "s_setCapColorMainDarkened", // - [&styleData] { styleData.setCapColor(CapColorMode::MainDarkened{}); }) + [&styleData] + { styleData.setCapColor(CapColor{CapColorMode::MainDarkened{}}); }) .doc( "Set the color of the center polygon to match the main style " "color, darkened."); addLuaFn(lua, "s_setCapColorByIndex", // [&styleData](int mIndex) - { styleData.setCapColor(CapColorMode::ByIndex{mIndex}); }) + { styleData.setCapColor(CapColor{CapColorMode::ByIndex{mIndex}}); }) .arg("index") .doc( "Set the color of the center polygon to match the style color with " @@ -1378,7 +1379,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, const std::size_t shaderId, const std::string& name, const float a) { withValidShaderId("shdr_setUniformF", shaderId, - [&](sf::Shader& shader) { shader.setUniform(name, a); }); + [&](sf::Shader& shader) { shader.setUniformUnsafe(name, a); }); }) .arg("shaderId") .arg("name") @@ -1393,7 +1394,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, { withValidShaderId("shdr_setUniformFVec2", shaderId, [&](sf::Shader& shader) { - shader.setUniform(name, sf::Glsl::Vec2{a, b}); + shader.setUniformUnsafe(name, sf::Glsl::Vec2{a, b}); }); }) .arg("shaderId") @@ -1410,7 +1411,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, { withValidShaderId("shdr_setUniformFVec3", shaderId, [&](sf::Shader& shader) { - shader.setUniform(name, sf::Glsl::Vec3{a, b, c}); + shader.setUniformUnsafe(name, sf::Glsl::Vec3{a, b, c}); }); }) .arg("shaderId") @@ -1428,7 +1429,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, { withValidShaderId("shdr_setUniformFVec4", shaderId, [&](sf::Shader& shader) { - shader.setUniform(name, sf::Glsl::Vec4{a, b, c, d}); + shader.setUniformUnsafe(name, sf::Glsl::Vec4{a, b, c, d}); }); }) .arg("shaderId") @@ -1449,7 +1450,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, const std::size_t shaderId, const std::string& name, const int a) { withValidShaderId("shdr_setUniformI", shaderId, - [&](sf::Shader& shader) { shader.setUniform(name, a); }); + [&](sf::Shader& shader) { shader.setUniformUnsafe(name, a); }); }) .arg("shaderId") .arg("name") @@ -1464,7 +1465,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, { withValidShaderId("shdr_setUniformIVec2", shaderId, [&](sf::Shader& shader) { - shader.setUniform(name, sf::Glsl::Ivec2{a, b}); + shader.setUniformUnsafe(name, sf::Glsl::Ivec2{a, b}); }); }) .arg("shaderId") @@ -1481,7 +1482,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, { withValidShaderId("shdr_setUniformIVec3", shaderId, [&](sf::Shader& shader) { - shader.setUniform(name, sf::Glsl::Ivec3{a, b, c}); + shader.setUniformUnsafe(name, sf::Glsl::Ivec3{a, b, c}); }); }) .arg("shaderId") @@ -1499,7 +1500,7 @@ static void initShaders(Lua::LuaContext& lua, HGAssets& assets, { withValidShaderId("shdr_setUniformIVec4", shaderId, [&](sf::Shader& shader) { - shader.setUniform(name, sf::Glsl::Ivec4{a, b, c, d}); + shader.setUniformUnsafe(name, sf::Glsl::Ivec4{a, b, c, d}); }); }) .arg("shaderId") diff --git a/src/SSVOpenHexagon/Core/MenuGame.cpp b/src/SSVOpenHexagon/Core/MenuGame.cpp index c88bac3d..00a35f40 100644 --- a/src/SSVOpenHexagon/Core/MenuGame.cpp +++ b/src/SSVOpenHexagon/Core/MenuGame.cpp @@ -1020,7 +1020,14 @@ void MenuGame::initInput() void MenuGame::runLuaFile(const std::string& mFileName) try { - Utils::runLuaFile(lua, mFileName); + if(Config::getUseLuaFileCache()) + { + Utils::runLuaFileCached(assets, lua, mFileName); + } + else + { + Utils::runLuaFile(lua, mFileName); + } } catch(...) { @@ -1153,12 +1160,14 @@ void MenuGame::initMenus() auto& resolution(optionsMenu.createCategory("resolution")); auto& gfx(optionsMenu.createCategory("graphics")); auto& sfx(optionsMenu.createCategory("audio")); + auto& advanced(optionsMenu.createCategory("advanced")); options.create<i::Goto>("GAMEPLAY", play); options.create<i::Goto>("CONTROLS", controls); options.create<i::Goto>("RESOLUTION", resolution); options.create<i::Goto>("GRAPHICS", gfx); options.create<i::Goto>("AUDIO", sfx); + options.create<i::Goto>("ADVANCED", advanced); options.create<i::Single>("RESET CONFIG", [this] { @@ -1506,6 +1515,17 @@ void MenuGame::initMenus() &Config::getPlaySwapReadySound, &Config::setPlaySwapReadySound); sfx.create<i::GoBack>("back"); + //-------------------------------- + // Advanced + + advanced.create<i::Toggle>("cache lua files", &Config::getUseLuaFileCache, + &Config::setUseLuaFileCache); + + advanced.create<i::Single>( + "clear lua file cache", [this] { assets.getLuaFileCache().clear(); }); + + advanced.create<i::Toggle>("disable game rendering", + &Config::getDisableGameRendering, &Config::setDisableGameRendering); //-------------------------------- // MAIN MENU @@ -3033,11 +3053,12 @@ void MenuGame::setIndex(const int mIdx) // Set gameplay values diffMultIdx = 0; for(; levelData->difficultyMults.at(diffMultIdx) != 1.f; ++diffMultIdx) - {} + { + } try { - Utils::runLuaFile(lua, levelData->luaScriptPath); + runLuaFile(levelData->luaScriptPath); Utils::runLuaFunctionIfExists<void>(lua, "onInit"); Utils::runLuaFunctionIfExists<void>(lua, "onLoad"); } diff --git a/src/SSVOpenHexagon/Core/Replay.cpp b/src/SSVOpenHexagon/Core/Replay.cpp index f440386a..e95026d0 100644 --- a/src/SSVOpenHexagon/Core/Replay.cpp +++ b/src/SSVOpenHexagon/Core/Replay.cpp @@ -8,8 +8,6 @@ #include "SSVOpenHexagon/Utils/Concat.hpp" #include "SSVOpenHexagon/Utils/Timestamp.hpp" -#include <SSVUtils/Core/Common/LikelyUnlikely.hpp> - #include <SFML/Network/Packet.hpp> #include <zlib.h> @@ -29,17 +27,17 @@ namespace hg { ::std::cerr << "Failed [de]serialization operation '" << code << "'\n"; } -#define SSVOH_TRY(...) \ - do \ - { \ - __VA_ARGS__; \ - \ - if(SSVU_UNLIKELY(!result._success)) \ - { \ - printTryFailure(#__VA_ARGS__); \ - return result; \ - } \ - } \ +#define SSVOH_TRY(...) \ + do \ + { \ + __VA_ARGS__; \ + \ + if(!result._success) [[unlikely]] \ + { \ + printTryFailure(#__VA_ARGS__); \ + return result; \ + } \ + } \ while(false) static auto make_write(serialization_result& result, std::byte*& buffer, diff --git a/src/SSVOpenHexagon/Core/Steam.cpp b/src/SSVOpenHexagon/Core/Steam.cpp index b46322c1..a5c10eb8 100644 --- a/src/SSVOpenHexagon/Core/Steam.cpp +++ b/src/SSVOpenHexagon/Core/Steam.cpp @@ -38,7 +38,7 @@ namespace hg::Steam { { // Using C API here because C++ one doesn't work with MinGW. - ISteamUser* steam_user = SteamAPI_SteamUser_v021(); + ISteamUser* steam_user = SteamAPI_SteamUser_v022(); if(!SteamAPI_ISteamUser_BLoggedOn(steam_user)) { diff --git a/src/SSVOpenHexagon/Core/main.cpp b/src/SSVOpenHexagon/Core/main.cpp index d4da8091..85edbe8e 100644 --- a/src/SSVOpenHexagon/Core/main.cpp +++ b/src/SSVOpenHexagon/Core/main.cpp @@ -275,6 +275,9 @@ getFirstCompressedReplayFilenameFromArgs(const std::vector<std::string>& args) hg::Config::loadConfig(args); hg::Config::reapplyResolution(); + // TODO (P0): call this in server as well + // TODO (P0): server gets ALSA errors during asset load, is it loading + // musics/sounds? HG_SCOPE_GUARD({ ssvu::lo("::main") << "Saving config...\n"; hg::Config::saveConfig(); @@ -385,7 +388,7 @@ getFirstCompressedReplayFilenameFromArgs(const std::vector<std::string>& args) [&assets](const std::string& assetId) -> sf::SoundBuffer* { return assets.getSoundBuffer(assetId); }, // [&assets](const std::string& assetId) -> const std::string* - { return assets.getMusicPath(assetId); } // + { return assets.getMusicPath(assetId); } // }; audio.setSoundVolume(hg::Config::getSoundVolume()); @@ -427,7 +430,7 @@ getFirstCompressedReplayFilenameFromArgs(const std::vector<std::string>& args) mg->fnHGTriggerRefresh = [&](const ssvs::Input::Trigger& trigger, int bindId) // { - hg.refreshTrigger(trigger, bindId); // + hg.refreshTrigger(trigger, bindId); // }; mg->fnHGNewGame = [&](const std::string& packId, diff --git a/src/SSVOpenHexagon/Data/CapColor.cpp b/src/SSVOpenHexagon/Data/CapColor.cpp index ebffc4f0..5cec160d 100644 --- a/src/SSVOpenHexagon/Data/CapColor.cpp +++ b/src/SSVOpenHexagon/Data/CapColor.cpp @@ -21,12 +21,12 @@ namespace hg { if(str == "main") { - return CapColorMode::Main{}; + return CapColor{CapColorMode::Main{}}; } if(str == "main_darkened") { - return CapColorMode::MainDarkened{}; + return CapColor{CapColorMode::MainDarkened{}}; } } @@ -36,22 +36,23 @@ namespace hg { if(legacy) { const int index = ssvuj::getExtr<int>(obj, "index", 0); - return CapColorMode::ByIndex{index}; + return CapColor{CapColorMode::ByIndex{index}}; } else { - return ColorData{false, ssvuj::getExtr<bool>(obj, "dynamic", false), - ssvuj::getExtr<bool>(obj, "dynamic_offset", false), - ssvuj::getExtr<float>(obj, "dynamic_darkness", 1.f), - ssvuj::getExtr<float>(obj, "hue_shift", 0.f), - ssvuj::getExtr<float>(obj, "offset", 0.f), - ssvuj::getExtr<sf::Color>(obj, "value", sf::Color::White), - hg::pulse_from_json(obj)}; + return CapColor{ + ColorData{false, ssvuj::getExtr<bool>(obj, "dynamic", false), + ssvuj::getExtr<bool>(obj, "dynamic_offset", false), + ssvuj::getExtr<float>(obj, "dynamic_darkness", 1.f), + ssvuj::getExtr<float>(obj, "hue_shift", 0.f), + ssvuj::getExtr<float>(obj, "offset", 0.f), + ssvuj::getExtr<sf::Color>(obj, "value", sf::Color::White), + hg::pulse_from_json(obj)}}; } } // Fallback case: - return CapColorMode::ByIndex{0}; + return CapColor{CapColorMode::ByIndex{0}}; } } // namespace hg diff --git a/src/SSVOpenHexagon/Data/StyleData.cpp b/src/SSVOpenHexagon/Data/StyleData.cpp index 0db0a06e..2b48832d 100644 --- a/src/SSVOpenHexagon/Data/StyleData.cpp +++ b/src/SSVOpenHexagon/Data/StyleData.cpp @@ -56,11 +56,11 @@ StyleData::StyleData(const ssvuj::Obj& mRoot) ssvuj::getExtr<float>(mRoot, "3D_perspective_multiplier", 1.f)}, _3dOverrideColor{ssvuj::getExtr<sf::Color>( mRoot, "3D_override_color", sf::Color::Transparent)}, - mainColorData{ssvuj::getObj(mRoot, "main")}, // + mainColorData{ssvuj::getObj(mRoot, "main")}, // playerColor{ colorDataFromObjOrDefault(mRoot, "player_color", mainColorData)}, // textColor{ - colorDataFromObjOrDefault(mRoot, "text_color", mainColorData)}, // + colorDataFromObjOrDefault(mRoot, "text_color", mainColorData)}, // wallColor{colorDataFromObjOrDefault(mRoot, "wall_color", mainColorData)}, capColor{parseCapColor(ssvuj::getObj(mRoot, "cap_color"))} { @@ -341,9 +341,8 @@ StyleData::getColors() const noexcept sf::Color StyleData::getCapColorResult() const noexcept { - return Utils::match( - capColor, // - [this](CapColorMode::Main) { return getMainColor(); }, // + return capColor.linear_match( // + [this](CapColorMode::Main) { return getMainColor(); }, // [this](CapColorMode::MainDarkened) { return Utils::getColorDarkened(getMainColor(), 1.4f); }, // [this](CapColorMode::ByIndex x) { return getColor(x._index); }, // diff --git a/src/SSVOpenHexagon/Global/Assets.cpp b/src/SSVOpenHexagon/Global/Assets.cpp index 051e582b..112d96ef 100644 --- a/src/SSVOpenHexagon/Global/Assets.cpp +++ b/src/SSVOpenHexagon/Global/Assets.cpp @@ -6,10 +6,17 @@ #include "SSVOpenHexagon/Core/Steam.hpp" +#include "SSVOpenHexagon/Data/LevelData.hpp" +#include "SSVOpenHexagon/Data/LoadInfo.hpp" #include "SSVOpenHexagon/Data/MusicData.hpp" +#include "SSVOpenHexagon/Data/PackData.hpp" +#include "SSVOpenHexagon/Data/PackInfo.hpp" +#include "SSVOpenHexagon/Data/ProfileData.hpp" +#include "SSVOpenHexagon/Data/StyleData.hpp" #include "SSVOpenHexagon/Global/Assert.hpp" #include "SSVOpenHexagon/Global/AssetStorage.hpp" +#include "SSVOpenHexagon/Global/Macros.hpp" #include "SSVOpenHexagon/Global/UtilsJson.hpp" #include "SSVOpenHexagon/Global/Version.hpp" @@ -36,6 +43,185 @@ namespace hg { +class HGAssets::HGAssetsImpl +{ +private: + Steam::steam_manager* steamManager; + + bool levelsOnly{false}; + + Utils::UniquePtr<AssetStorage> assetStorage; + + std::unordered_map<std::string, LevelData> levelDatas; + std::unordered_map<std::string, std::vector<std::string>> + levelDataIdsByPack; + + std::unordered_map<std::string, PackData> packDatas; + + std::vector<PackInfo> packInfos; + std::vector<PackInfo> selectablePackInfos; + + std::unordered_map<std::string, std::string> musicPathMap; + std::map<std::string, MusicData> musicDataMap; + std::map<std::string, StyleData> styleDataMap; + std::map<std::string, ProfileData> profileDataMap; + ProfileData* currentProfilePtr{nullptr}; + + std::unordered_set<std::string> packIdsWithMissingDependencies; + + struct LoadedShader + { + Utils::UniquePtr<sf::Shader> shader; + std::string path; + sf::Shader::Type shaderType; + std::size_t id; + }; + + std::unordered_map<std::string, LoadedShader> shaders; + std::unordered_map<std::string, std::size_t> shadersPathToId; + std::vector<sf::Shader*> shadersById; + + std::string buf; + + std::unordered_map<std::string, std::string> luaFileCache; + LoadInfo loadInfo; + + // When the Steam API can not be retrieved, this set holds pack ids + // retrieved from the cache to try and load the workshop packs installed + std::unordered_set<std::string> cachedWorkshopPackIds; + + template <typename... Ts> + [[nodiscard]] std::string& concatIntoBuf(const Ts&...); + + [[nodiscard]] bool loadAllPackDatas(); + [[nodiscard]] bool loadAllPackAssets(const bool headless); + [[nodiscard]] bool loadWorkshopPackDatasFromCache(); + [[nodiscard]] bool verifyAllPackDependencies(); + [[nodiscard]] bool loadAllLocalProfiles(); + + [[nodiscard]] bool loadPackData(const ssvufs::Path& packPath); + + [[nodiscard]] bool loadPackAssets( + const PackData& packData, const bool headless); + + void loadPackAssets_loadShaders(const std::string& mPackId, + const ssvufs::Path& mPath, const bool headless); + void loadPackAssets_loadMusic( + const std::string& mPackId, const ssvufs::Path& mPath); + void loadPackAssets_loadMusicData( + const std::string& mPackId, const ssvufs::Path& mPath); + void loadPackAssets_loadStyleData( + const std::string& mPackId, const ssvufs::Path& mPath); + void loadPackAssets_loadLevelData( + const std::string& mPackId, const ssvufs::Path& mPath); + void loadPackAssets_loadCustomSounds( + const std::string& mPackId, const ssvufs::Path& mPath); + + [[nodiscard]] std::string getCurrentLocalProfileFilePath(); + +public: + HGAssetsImpl(Steam::steam_manager* mSteamManager, bool mHeadless, + bool mLevelsOnly = false); + + ~HGAssetsImpl(); + + [[nodiscard]] LoadInfo& getLoadResults(); + + [[nodiscard]] sf::Texture& getTexture(const std::string& mId); + [[nodiscard]] sf::Texture& getTextureOrNullTexture(const std::string& mId); + + [[nodiscard]] sf::Font& getFont(const std::string& mId); + [[nodiscard]] sf::Font& getFontOrNullFont(const std::string& mId); + + [[nodiscard]] bool isValidLevelId( + const std::string& mLevelId) const noexcept; + + [[nodiscard]] const LevelData& getLevelData( + const std::string& mAssetId) const; + + [[nodiscard]] bool packHasLevels(const std::string& mPackId); + + [[nodiscard]] const std::vector<std::string>& getLevelIdsByPack( + const std::string& mPackId); + + [[nodiscard]] const std::unordered_map<std::string, PackData>& + getPackDatas(); + + [[nodiscard]] bool isValidPackId(const std::string& mPackId) const noexcept; + + [[nodiscard]] const PackData& getPackData(const std::string& mPackId); + + [[nodiscard]] const std::vector<PackInfo>& + getSelectablePackInfos() const noexcept; + + [[nodiscard]] const PackData* findPackData( + const std::string& mPackDisambiguator, const std::string& mPackName, + const std::string& mPackAuthor) const noexcept; + + [[nodiscard]] const MusicData& getMusicData( + const std::string& mPackId, const std::string& mId); + [[nodiscard]] const StyleData& getStyleData( + const std::string& mPackId, const std::string& mId); + [[nodiscard]] sf::Shader* getShader( + const std::string& mPackId, const std::string& mId); + + [[nodiscard]] std::optional<std::size_t> getShaderId( + const std::string& mPackId, const std::string& mId); + [[nodiscard]] std::optional<std::size_t> getShaderIdByPath( + const std::string& mShaderPath); + [[nodiscard]] sf::Shader* getShaderByShaderId(const std::size_t mShaderId); + [[nodiscard]] bool isValidShaderId(const std::size_t mShaderId) const; + + void reloadAllShaders(); + [[nodiscard]] std::string reloadPack( + const std::string& mPackId, const std::string& mPath); + [[nodiscard]] std::string reloadLevel(const std::string& mPackId, + const std::string& mPath, const std::string& mId); + + [[nodiscard]] float getLocalScore(const std::string& mId); + void setLocalScore(const std::string& mId, float mScore); + + void saveCurrentLocalProfile(); + void saveAllProfiles(); + + [[nodiscard]] bool anyLocalProfileActive() const; + [[nodiscard]] ProfileData& getCurrentLocalProfile(); + [[nodiscard]] const ProfileData& getCurrentLocalProfile() const; + [[nodiscard]] ProfileData* getLocalProfileByName(const std::string& mName); + [[nodiscard]] const ProfileData* getLocalProfileByName( + const std::string& mName) const; + [[nodiscard]] std::size_t getLocalProfilesSize(); + [[nodiscard]] std::vector<std::string> getLocalProfileNames(); + + [[nodiscard]] bool pIsValidLocalProfile() const; + [[nodiscard]] const std::string& pGetName() const; + + void pSaveCurrent(); + void pSaveAll(); + void pSetCurrent(const std::string& mName); + void pCreate(const std::string& mName); + void pRemove(const std::string& mName); + + [[nodiscard]] sf::SoundBuffer* getSoundBuffer(const std::string& assetId); + + [[nodiscard]] const std::string* getMusicPath( + const std::string& assetId) const; + + [[nodiscard]] const std::unordered_map<std::string, LevelData>& + getLevelDatas() const noexcept; + + [[nodiscard]] const std::unordered_set<std::string>& + getPackIdsWithMissingDependencies() const noexcept; + + void addLocalProfile(ProfileData&& profileData); + + [[nodiscard]] std::unordered_map<std::string, std::string>& + getLuaFileCache(); + + [[nodiscard]] const std::unordered_map<std::string, std::string>& + getLuaFileCache() const; +}; + static void loadAssetsFromJson(AssetStorage& assetStorage, const ssvu::FileSystem::Path& mRootPath, const ssvuj::Obj& mObj) { @@ -111,14 +297,15 @@ static void loadAssetsFromJson(AssetStorage& assetStorage, } template <typename... Ts> -[[nodiscard]] std::string& HGAssets::concatIntoBuf(const Ts&... xs) +[[nodiscard]] std::string& HGAssets::HGAssetsImpl::concatIntoBuf( + const Ts&... xs) { buf.clear(); Utils::concatInto(buf, xs...); return buf; } -HGAssets::HGAssets( +HGAssets::HGAssetsImpl::HGAssetsImpl( Steam::steam_manager* mSteamManager, bool mHeadless, bool mLevelsOnly) : steamManager{mSteamManager}, levelsOnly{mLevelsOnly}, @@ -203,12 +390,13 @@ HGAssets::HGAssets( << "ms\n"; } -HGAssets::~HGAssets() +HGAssets::HGAssetsImpl::~HGAssetsImpl() { ssvu::lo("HGAssets::~HGAssets") << "Cleaning up assets...\n"; } -[[nodiscard]] bool HGAssets::loadPackData(const ssvufs::Path& packPath) +[[nodiscard]] bool HGAssets::HGAssetsImpl::loadPackData( + const ssvufs::Path& packPath) { if(!ssvufs::Path{packPath + "/pack.json"}.isFile()) { @@ -274,23 +462,23 @@ HGAssets::~HGAssets() packInfos.emplace_back(PackInfo{packId, packPath}); - packDatas.emplace(packId, // + packDatas.emplace(packId, // PackData{ - .folderPath{packPath.getStr()}, // - .id{packId}, // - .disambiguator{std::move(packDisambiguator)}, // - .name{std::move(packName)}, // - .author{std::move(packAuthor)}, // - .description{std::move(packDescription)}, // - .version{packVersion}, // - .priority{packPriority}, // - .dependencies{getPackDependencies()} // + .folderPath{packPath.getStr()}, // + .id{packId}, // + .disambiguator{SSVOH_MOVE(packDisambiguator)}, // + .name{SSVOH_MOVE(packName)}, // + .author{SSVOH_MOVE(packAuthor)}, // + .description{SSVOH_MOVE(packDescription)}, // + .version{packVersion}, // + .priority{packPriority}, // + .dependencies{getPackDependencies()} // }); return true; } -[[nodiscard]] bool HGAssets::loadPackAssets( +[[nodiscard]] bool HGAssets::HGAssetsImpl::loadPackAssets( const PackData& packData, const bool headless) { const std::string& packPath{packData.folderPath}; @@ -363,12 +551,13 @@ HGAssets::~HGAssets() //********************************************** // LOAD -[[nodiscard]] LoadInfo& HGAssets::getLoadResults() +[[nodiscard]] LoadInfo& HGAssets::HGAssetsImpl::getLoadResults() { return loadInfo; } -[[nodiscard]] sf::Texture& HGAssets::getTexture(const std::string& mId) +[[nodiscard]] sf::Texture& HGAssets::HGAssetsImpl::getTexture( + const std::string& mId) { sf::Texture* ptr = assetStorage->getTexture(mId); SSVOH_ASSERT(ptr); @@ -376,7 +565,7 @@ HGAssets::~HGAssets() return *ptr; } -[[nodiscard]] sf::Texture& HGAssets::getTextureOrNullTexture( +[[nodiscard]] sf::Texture& HGAssets::HGAssetsImpl::getTextureOrNullTexture( const std::string& mId) { static sf::Texture nullTexture; @@ -385,7 +574,7 @@ HGAssets::~HGAssets() return ptr ? *ptr : nullTexture; } -[[nodiscard]] sf::Font& HGAssets::getFont(const std::string& mId) +[[nodiscard]] sf::Font& HGAssets::HGAssetsImpl::getFont(const std::string& mId) { sf::Font* ptr = assetStorage->getFont(mId); SSVOH_ASSERT(ptr); @@ -393,7 +582,8 @@ HGAssets::~HGAssets() return *ptr; } -[[nodiscard]] sf::Font& HGAssets::getFontOrNullFont(const std::string& mId) +[[nodiscard]] sf::Font& HGAssets::HGAssetsImpl::getFontOrNullFont( + const std::string& mId) { static sf::Font nullFont; sf::Font* ptr = assetStorage->getFont(mId); @@ -401,56 +591,58 @@ HGAssets::~HGAssets() return ptr ? *ptr : nullFont; } -[[nodiscard]] bool HGAssets::isValidLevelId( +[[nodiscard]] bool HGAssets::HGAssetsImpl::isValidLevelId( const std::string& mLevelId) const noexcept { return levelDatas.find(mLevelId) != levelDatas.end(); } -[[nodiscard]] const LevelData& HGAssets::getLevelData( +[[nodiscard]] const LevelData& HGAssets::HGAssetsImpl::getLevelData( const std::string& mAssetId) const { SSVOH_ASSERT(isValidLevelId(mAssetId)); return levelDatas.at(mAssetId); } -[[nodiscard]] bool HGAssets::packHasLevels(const std::string& mPackId) +[[nodiscard]] bool HGAssets::HGAssetsImpl::packHasLevels( + const std::string& mPackId) { return levelDataIdsByPack.count(mPackId) > 0; } -[[nodiscard]] const std::vector<std::string>& HGAssets::getLevelIdsByPack( - const std::string& mPackId) +[[nodiscard]] const std::vector<std::string>& +HGAssets::HGAssetsImpl::getLevelIdsByPack(const std::string& mPackId) { SSVOH_ASSERT(levelDataIdsByPack.count(mPackId) > 0); return levelDataIdsByPack.at(mPackId); } [[nodiscard]] const std::unordered_map<std::string, PackData>& -HGAssets::getPackDatas() +HGAssets::HGAssetsImpl::getPackDatas() { return packDatas; } -[[nodiscard]] bool HGAssets::isValidPackId( +[[nodiscard]] bool HGAssets::HGAssetsImpl::isValidPackId( const std::string& mPackId) const noexcept { return packDatas.find(mPackId) != packDatas.end(); } -[[nodiscard]] const PackData& HGAssets::getPackData(const std::string& mPackId) +[[nodiscard]] const PackData& HGAssets::HGAssetsImpl::getPackData( + const std::string& mPackId) { SSVOH_ASSERT(isValidPackId(mPackId)); return packDatas.at(mPackId); } [[nodiscard]] const std::vector<PackInfo>& -HGAssets::getSelectablePackInfos() const noexcept +HGAssets::HGAssetsImpl::getSelectablePackInfos() const noexcept { return selectablePackInfos; } -[[nodiscard]] const PackData* HGAssets::findPackData( +[[nodiscard]] const PackData* HGAssets::HGAssetsImpl::findPackData( const std::string& mPackDisambiguator, const std::string& mPackName, const std::string& mPackAuthor) const noexcept { @@ -467,7 +659,7 @@ HGAssets::getSelectablePackInfos() const noexcept return nullptr; } -[[nodiscard]] bool HGAssets::loadWorkshopPackDatasFromCache() +[[nodiscard]] bool HGAssets::HGAssetsImpl::loadWorkshopPackDatasFromCache() { if(!ssvufs::Path{"workshopCache.json"}.isFile()) { @@ -520,7 +712,7 @@ HGAssets::getSelectablePackInfos() const noexcept return true; } -[[nodiscard]] bool HGAssets::loadAllPackDatas() +[[nodiscard]] bool HGAssets::HGAssetsImpl::loadAllPackDatas() { if(!ssvufs::Path{"Packs/"}.isFolder()) { @@ -575,7 +767,8 @@ HGAssets::getSelectablePackInfos() const noexcept return true; } -[[nodiscard]] bool HGAssets::loadAllPackAssets(const bool headless) +[[nodiscard]] bool HGAssets::HGAssetsImpl::loadAllPackAssets( + const bool headless) { for(const auto& [packId, packData] : packDatas) { @@ -596,7 +789,7 @@ HGAssets::getSelectablePackInfos() const noexcept return true; } -[[nodiscard]] bool HGAssets::verifyAllPackDependencies() +[[nodiscard]] bool HGAssets::HGAssetsImpl::verifyAllPackDependencies() { // ------------------------------------------------------------------------ // Verify pack dependencies. @@ -643,17 +836,17 @@ HGAssets::getSelectablePackInfos() const noexcept return true; } -void HGAssets::addLocalProfile(ProfileData&& profileData) +void HGAssets::HGAssetsImpl::addLocalProfile(ProfileData&& profileData) { // Remove invalid level ids that might have been added to the files. Utils::erase_if(profileData.getFavoriteLevelIds(), [this](const std::string& favId) { return levelDatas.find(favId) == levelDatas.end(); }); - profileDataMap.emplace(profileData.getName(), std::move(profileData)); + profileDataMap.emplace(profileData.getName(), SSVOH_MOVE(profileData)); } -[[nodiscard]] bool HGAssets::loadAllLocalProfiles() +[[nodiscard]] bool HGAssets::HGAssetsImpl::loadAllLocalProfiles() { if(!ssvufs::Path{"Profiles/"}.isFolder()) { @@ -671,13 +864,13 @@ void HGAssets::addLocalProfile(ProfileData&& profileData) loadInfo.addFormattedError(error); ProfileData profileData{Utils::loadProfileFromJson(object)}; - addLocalProfile(std::move(profileData)); + addLocalProfile(SSVOH_MOVE(profileData)); } return true; } -void HGAssets::loadPackAssets_loadShaders( +void HGAssets::HGAssetsImpl::loadPackAssets_loadShaders( const std::string& mPackId, const ssvufs::Path& mPath, const bool headless) { if(headless) @@ -705,13 +898,13 @@ void HGAssets::loadPackAssets_loadShaders( SSVOH_ASSERT(shadersById.size() > 0); const std::size_t shaderId = shadersById.size() - 1; - LoadedShader ls{.shader{std::move(shader)}, + LoadedShader ls{.shader{SSVOH_MOVE(shader)}, .path{p}, .shaderType{shaderType}, .id{shaderId}}; shaders.emplace( - concatIntoBuf(mPackId, '_', p.getFileName()), std::move(ls)); + concatIntoBuf(mPackId, '_', p.getFileName()), SSVOH_MOVE(ls)); shadersPathToId.emplace(p, shaderId); @@ -724,7 +917,7 @@ void HGAssets::loadPackAssets_loadShaders( loadShadersOfType(".frag", sf::Shader::Type::Fragment); } -void HGAssets::loadPackAssets_loadCustomSounds( +void HGAssets::HGAssetsImpl::loadPackAssets_loadCustomSounds( const std::string& mPackId, const ssvufs::Path& mPath) { for(const auto& p : scanSingleByExt(mPath + "Sounds/", ".ogg")) @@ -740,7 +933,7 @@ void HGAssets::loadPackAssets_loadCustomSounds( } } -void HGAssets::loadPackAssets_loadMusic( +void HGAssets::HGAssetsImpl::loadPackAssets_loadMusic( const std::string& mPackId, const ssvufs::Path& mPath) { for(const auto& p : scanSingleByExt(mPath + "Music/", ".ogg")) @@ -752,7 +945,7 @@ void HGAssets::loadPackAssets_loadMusic( } } -void HGAssets::loadPackAssets_loadMusicData( +void HGAssets::HGAssetsImpl::loadPackAssets_loadMusicData( const std::string& mPackId, const ssvufs::Path& mPath) { for(const auto& p : scanSingleByExt(mPath + "Music/", ".json")) @@ -762,13 +955,13 @@ void HGAssets::loadPackAssets_loadMusicData( MusicData musicData{Utils::loadMusicFromJson(object)}; musicDataMap.emplace( - concatIntoBuf(mPackId, '_', musicData.id), std::move(musicData)); + concatIntoBuf(mPackId, '_', musicData.id), SSVOH_MOVE(musicData)); ++loadInfo.assets; } } -void HGAssets::loadPackAssets_loadStyleData( +void HGAssets::HGAssetsImpl::loadPackAssets_loadStyleData( const std::string& mPackId, const ssvufs::Path& mPath) { for(const auto& p : scanSingleByExt(mPath + "Styles/", ".json")) @@ -778,13 +971,13 @@ void HGAssets::loadPackAssets_loadStyleData( StyleData styleData{object}; styleDataMap.emplace( - concatIntoBuf(mPackId, '_', styleData.id), std::move(styleData)); + concatIntoBuf(mPackId, '_', styleData.id), SSVOH_MOVE(styleData)); ++loadInfo.assets; } } -void HGAssets::loadPackAssets_loadLevelData( +void HGAssets::HGAssetsImpl::loadPackAssets_loadLevelData( const std::string& mPackId, const ssvufs::Path& mPath) { for(const auto& p : scanSingleByExt(mPath + "Levels/", ".json")) @@ -796,7 +989,7 @@ void HGAssets::loadPackAssets_loadLevelData( const std::string& assetId = concatIntoBuf(mPackId, '_', levelData.id); levelDataIdsByPack[mPackId].emplace_back(assetId); - levelDatas.emplace(std::move(assetId), std::move(levelData)); + levelDatas.emplace(assetId, SSVOH_MOVE(levelData)); ++loadInfo.levels; } @@ -805,7 +998,7 @@ void HGAssets::loadPackAssets_loadLevelData( //********************************************** // PROFILE -void HGAssets::saveCurrentLocalProfile() +void HGAssets::HGAssetsImpl::saveCurrentLocalProfile() { if(currentProfilePtr == nullptr) { @@ -831,7 +1024,7 @@ void HGAssets::saveCurrentLocalProfile() ssvuj::writeToFile(profileRoot, getCurrentLocalProfileFilePath()); } -void HGAssets::saveAllProfiles() +void HGAssets::HGAssetsImpl::saveAllProfiles() { ssvuj::Obj currentVersion; @@ -864,7 +1057,7 @@ void HGAssets::saveAllProfiles() //********************************************** // GET -[[nodiscard]] const MusicData& HGAssets::getMusicData( +[[nodiscard]] const MusicData& HGAssets::HGAssetsImpl::getMusicData( const std::string& mPackId, const std::string& mId) { const std::string& assetId = concatIntoBuf(mPackId, '_', mId); @@ -881,7 +1074,7 @@ void HGAssets::saveAllProfiles() return it->second; } -[[nodiscard]] const StyleData& HGAssets::getStyleData( +[[nodiscard]] const StyleData& HGAssets::HGAssetsImpl::getStyleData( const std::string& mPackId, const std::string& mId) { const std::string& assetId = concatIntoBuf(mPackId, '_', mId); @@ -898,7 +1091,7 @@ void HGAssets::saveAllProfiles() return it->second; } -[[nodiscard]] sf::Shader* HGAssets::getShader( +[[nodiscard]] sf::Shader* HGAssets::HGAssetsImpl::getShader( const std::string& mPackId, const std::string& mId) { const std::string& assetId = concatIntoBuf(mPackId, '_', mId); @@ -913,7 +1106,7 @@ void HGAssets::saveAllProfiles() return it->second.shader.get(); } -[[nodiscard]] std::optional<std::size_t> HGAssets::getShaderId( +[[nodiscard]] std::optional<std::size_t> HGAssets::HGAssetsImpl::getShaderId( const std::string& mPackId, const std::string& mId) { const std::string& assetId = concatIntoBuf(mPackId, '_', mId); @@ -928,8 +1121,8 @@ void HGAssets::saveAllProfiles() return it->second.id; } -[[nodiscard]] std::optional<std::size_t> HGAssets::getShaderIdByPath( - const std::string& mShaderPath) +[[nodiscard]] std::optional<std::size_t> +HGAssets::HGAssetsImpl::getShaderIdByPath(const std::string& mShaderPath) { const auto it = shadersPathToId.find(mShaderPath); if(it == shadersPathToId.end()) @@ -943,7 +1136,7 @@ void HGAssets::saveAllProfiles() return it->second; } -[[nodiscard]] sf::Shader* HGAssets::getShaderByShaderId( +[[nodiscard]] sf::Shader* HGAssets::HGAssetsImpl::getShaderByShaderId( const std::size_t mShaderId) { if(!isValidShaderId(mShaderId)) @@ -954,7 +1147,8 @@ void HGAssets::saveAllProfiles() return shadersById[mShaderId]; } -[[nodiscard]] bool HGAssets::isValidShaderId(const std::size_t mShaderId) const +[[nodiscard]] bool HGAssets::HGAssetsImpl::isValidShaderId( + const std::size_t mShaderId) const { return mShaderId < shadersById.size(); } @@ -962,14 +1156,14 @@ void HGAssets::saveAllProfiles() //********************************************** // RELOAD -void HGAssets::reloadAllShaders() +void HGAssets::HGAssetsImpl::reloadAllShaders() { for(auto& [id, loadedShader] : shaders) { if(!loadedShader.shader->loadFromFile( loadedShader.path, loadedShader.shaderType)) { - ssvu::lo("hg::HGAssets::reloadAllShaders") + ssvu::lo("hg::HGAssetsImplImpl::reloadAllShaders") << "Failed to load shader '" << loadedShader.path << "'\n"; continue; @@ -977,7 +1171,7 @@ void HGAssets::reloadAllShaders() } } -[[nodiscard]] std::string HGAssets::reloadPack( +[[nodiscard]] std::string HGAssets::HGAssetsImpl::reloadPack( const std::string& mPackId, const std::string& mPath) { std::string temp, output; @@ -997,7 +1191,7 @@ void HGAssets::reloadAllShaders() if(it == levelDatas.end()) { levelDataIdsByPack[mPackId].emplace_back(temp); - levelDatas.emplace(temp, std::move(levelData)); + levelDatas.emplace(temp, SSVOH_MOVE(levelData)); } else { @@ -1019,7 +1213,7 @@ void HGAssets::reloadAllShaders() StyleData styleData{ssvuj::getFromFile(p)}; temp = mPackId + "_" + styleData.id; - styleDataMap[temp] = std::move(styleData); + styleDataMap[temp] = SSVOH_MOVE(styleData); } output += "Styles successfully reloaded\n"; } @@ -1038,7 +1232,7 @@ void HGAssets::reloadAllShaders() Utils::loadMusicFromJson(ssvuj::getFromFile(p))}; temp = mPackId + "_" + musicData.id; - musicDataMap[temp] = std::move(musicData); + musicDataMap[temp] = SSVOH_MOVE(musicData); } output += "Music data successfully reloaded\n"; } @@ -1083,8 +1277,9 @@ void HGAssets::reloadAllShaders() return output; } -[[nodiscard]] std::string HGAssets::reloadLevel(const std::string& mPackId, - const std::string& mPath, const std::string& mId) +[[nodiscard]] std::string HGAssets::HGAssetsImpl::reloadLevel( + const std::string& mPackId, const std::string& mPath, + const std::string& mId) { std::string temp, output; @@ -1111,7 +1306,7 @@ void HGAssets::reloadAllShaders() if(it == levelDatas.end()) { levelDataIdsByPack[mPackId].emplace_back(temp); - levelDatas.emplace(temp, std::move(levelData)); + levelDatas.emplace(temp, SSVOH_MOVE(levelData)); } else { @@ -1138,7 +1333,7 @@ void HGAssets::reloadAllShaders() StyleData styleData{ssvuj::getFromFile(styleFile[0])}; temp = mPackId + "_" + levelData.styleId; - styleDataMap[temp] = std::move(styleData); + styleDataMap[temp] = SSVOH_MOVE(styleData); output += "style data " + levelData.styleId + ".json successfully loaded\n"; @@ -1166,7 +1361,7 @@ void HGAssets::reloadAllShaders() Utils::loadMusicFromJson(ssvuj::getFromFile(musicDataFile[0]))}; temp = mPackId + "_" + levelData.musicId; - musicDataMap[temp] = std::move(musicData); + musicDataMap[temp] = SSVOH_MOVE(musicData); output += "music data " + levelData.musicId + ".json successfully loaded\n"; @@ -1248,12 +1443,12 @@ void HGAssets::reloadAllShaders() //********************************************** // LOCAL SCORE -float HGAssets::getLocalScore(const std::string& mId) +float HGAssets::HGAssetsImpl::getLocalScore(const std::string& mId) { return getCurrentLocalProfile().getScore(mId); } -void HGAssets::setLocalScore(const std::string& mId, float mScore) +void HGAssets::HGAssetsImpl::setLocalScore(const std::string& mId, float mScore) { getCurrentLocalProfile().setScore(mId, mScore); } @@ -1261,79 +1456,85 @@ void HGAssets::setLocalScore(const std::string& mId, float mScore) //********************************************** // LOCAL PROFILE -[[nodiscard]] bool HGAssets::anyLocalProfileActive() const +[[nodiscard]] bool HGAssets::HGAssetsImpl::anyLocalProfileActive() const { return currentProfilePtr != nullptr; } -ProfileData& HGAssets::getCurrentLocalProfile() +ProfileData& HGAssets::HGAssetsImpl::getCurrentLocalProfile() { SSVOH_ASSERT(currentProfilePtr != nullptr); return *currentProfilePtr; } -ProfileData* HGAssets::getLocalProfileByName(const std::string& mName) +ProfileData* HGAssets::HGAssetsImpl::getLocalProfileByName( + const std::string& mName) { SSVOH_ASSERT(profileDataMap.contains(mName)); return &profileDataMap.find(mName)->second; } -const ProfileData& HGAssets::getCurrentLocalProfile() const +const ProfileData& HGAssets::HGAssetsImpl::getCurrentLocalProfile() const { SSVOH_ASSERT(currentProfilePtr != nullptr); return *currentProfilePtr; } -const ProfileData* HGAssets::getLocalProfileByName( +const ProfileData* HGAssets::HGAssetsImpl::getLocalProfileByName( const std::string& mName) const { SSVOH_ASSERT(profileDataMap.contains(mName)); return &profileDataMap.find(mName)->second; } -[[nodiscard]] std::string HGAssets::getCurrentLocalProfileFilePath() +[[nodiscard]] std::string +HGAssets::HGAssetsImpl::getCurrentLocalProfileFilePath() { return "Profiles/" + currentProfilePtr->getName() + ".json"; } -[[nodiscard]] std::size_t HGAssets::getLocalProfilesSize() +[[nodiscard]] std::size_t HGAssets::HGAssetsImpl::getLocalProfilesSize() { return profileDataMap.size(); } -[[nodiscard]] std::vector<std::string> HGAssets::getLocalProfileNames() +[[nodiscard]] std::vector<std::string> +HGAssets::HGAssetsImpl::getLocalProfileNames() { std::vector<std::string> result; + result.reserve(profileDataMap.size()); + for(auto& pair : profileDataMap) { result.emplace_back(pair.second.getName()); } + return result; } -[[nodiscard]] bool HGAssets::pIsValidLocalProfile() const +[[nodiscard]] bool HGAssets::HGAssetsImpl::pIsValidLocalProfile() const { return currentProfilePtr != nullptr; } -[[nodiscard]] const std::string& HGAssets::pGetName() const +[[nodiscard]] const std::string& HGAssets::HGAssetsImpl::pGetName() const { return getCurrentLocalProfile().getName(); } -void HGAssets::pSaveCurrent() +void HGAssets::HGAssetsImpl::pSaveCurrent() { saveCurrentLocalProfile(); } -void HGAssets::pSaveAll() +void HGAssets::HGAssetsImpl::pSaveAll() { saveAllProfiles(); } -void HGAssets::pSetCurrent(const std::string& mName) +void HGAssets::HGAssetsImpl::pSetCurrent(const std::string& mName) { const auto it = profileDataMap.find(mName); @@ -1341,7 +1542,7 @@ void HGAssets::pSetCurrent(const std::string& mName) currentProfilePtr = &it->second; } -void HGAssets::pCreate(const std::string& mName) +void HGAssets::HGAssetsImpl::pCreate(const std::string& mName) { ssvuj::Obj root; ssvuj::arch(root, "name", mName); @@ -1359,18 +1560,18 @@ void HGAssets::pCreate(const std::string& mName) } } -void HGAssets::pRemove(const std::string& mName) +void HGAssets::HGAssetsImpl::pRemove(const std::string& mName) { profileDataMap.erase(mName); } -[[nodiscard]] sf::SoundBuffer* HGAssets::getSoundBuffer( +[[nodiscard]] sf::SoundBuffer* HGAssets::HGAssetsImpl::getSoundBuffer( const std::string& assetId) { return assetStorage->getSoundBuffer(assetId); } -[[nodiscard]] const std::string* HGAssets::getMusicPath( +[[nodiscard]] const std::string* HGAssets::HGAssetsImpl::getMusicPath( const std::string& assetId) const { auto it = musicPathMap.find(assetId); @@ -1378,15 +1579,296 @@ void HGAssets::pRemove(const std::string& mName) } [[nodiscard]] const std::unordered_map<std::string, LevelData>& -HGAssets::getLevelDatas() const noexcept +HGAssets::HGAssetsImpl::getLevelDatas() const noexcept { return levelDatas; } [[nodiscard]] const std::unordered_set<std::string>& -HGAssets::getPackIdsWithMissingDependencies() const noexcept +HGAssets::HGAssetsImpl::getPackIdsWithMissingDependencies() const noexcept { return packIdsWithMissingDependencies; } +[[nodiscard]] std::unordered_map<std::string, std::string>& +HGAssets::HGAssetsImpl::getLuaFileCache() +{ + return luaFileCache; +} + +[[nodiscard]] const std::unordered_map<std::string, std::string>& +HGAssets::HGAssetsImpl::getLuaFileCache() const +{ + return luaFileCache; +} + +// ---------------------------------------------------------------------------- + +HGAssets::HGAssets( + Steam::steam_manager* mSteamManager, bool mHeadless, bool mLevelsOnly) + : _impl(Utils::makeUnique<HGAssetsImpl>( + mSteamManager, mHeadless, mLevelsOnly)) +{} + +HGAssets::~HGAssets() = default; + +LoadInfo& HGAssets::getLoadResults() +{ + return _impl->getLoadResults(); +} + +sf::Texture& HGAssets::getTexture(const std::string& mId) +{ + return _impl->getTexture(mId); +} + +sf::Texture& HGAssets::getTextureOrNullTexture(const std::string& mId) +{ + return _impl->getTextureOrNullTexture(mId); +} + +sf::Font& HGAssets::getFont(const std::string& mId) +{ + return _impl->getFont(mId); +} + +sf::Font& HGAssets::getFontOrNullFont(const std::string& mId) +{ + return _impl->getFontOrNullFont(mId); +} + +bool HGAssets::isValidLevelId(const std::string& mLevelId) const noexcept +{ + return _impl->isValidLevelId(mLevelId); +} + +const LevelData& HGAssets::getLevelData(const std::string& mAssetId) const +{ + return _impl->getLevelData(mAssetId); +} + +bool HGAssets::packHasLevels(const std::string& mPackId) +{ + return _impl->packHasLevels(mPackId); +} + +const std::vector<std::string>& HGAssets::getLevelIdsByPack( + const std::string& mPackId) +{ + return _impl->getLevelIdsByPack(mPackId); +} + +const std::unordered_map<std::string, PackData>& HGAssets::getPackDatas() +{ + return _impl->getPackDatas(); +} + +bool HGAssets::isValidPackId(const std::string& mPackId) const noexcept +{ + return _impl->isValidPackId(mPackId); +} + +const PackData& HGAssets::getPackData(const std::string& mPackId) +{ + return _impl->getPackData(mPackId); +} + +const std::vector<PackInfo>& HGAssets::getSelectablePackInfos() const noexcept +{ + return _impl->getSelectablePackInfos(); +} + +const PackData* HGAssets::findPackData(const std::string& mPackDisambiguator, + const std::string& mPackName, const std::string& mPackAuthor) const noexcept +{ + return _impl->findPackData(mPackDisambiguator, mPackName, mPackAuthor); +} + +const MusicData& HGAssets::getMusicData( + const std::string& mPackId, const std::string& mId) +{ + return _impl->getMusicData(mPackId, mId); +} + +const StyleData& HGAssets::getStyleData( + const std::string& mPackId, const std::string& mId) +{ + return _impl->getStyleData(mPackId, mId); +} + +sf::Shader* HGAssets::getShader( + const std::string& mPackId, const std::string& mId) +{ + return _impl->getShader(mPackId, mId); +} + +std::optional<std::size_t> HGAssets::getShaderId( + const std::string& mPackId, const std::string& mId) +{ + return _impl->getShaderId(mPackId, mId); +} + +std::optional<std::size_t> HGAssets::getShaderIdByPath( + const std::string& mShaderPath) +{ + return _impl->getShaderIdByPath(mShaderPath); +} + +sf::Shader* HGAssets::getShaderByShaderId(const std::size_t mShaderId) +{ + return _impl->getShaderByShaderId(mShaderId); +} + +bool HGAssets::isValidShaderId(const std::size_t mShaderId) const +{ + return _impl->isValidShaderId(mShaderId); +} + +void HGAssets::reloadAllShaders() +{ + return _impl->reloadAllShaders(); +} + +std::string HGAssets::reloadPack( + const std::string& mPackId, const std::string& mPath) +{ + return _impl->reloadPack(mPackId, mPath); +} + +std::string HGAssets::reloadLevel(const std::string& mPackId, + const std::string& mPath, const std::string& mId) +{ + return _impl->reloadLevel(mPackId, mPath, mId); +} + +float HGAssets::getLocalScore(const std::string& mId) +{ + return _impl->getLocalScore(mId); +} + +void HGAssets::setLocalScore(const std::string& mId, float mScore) +{ + return _impl->setLocalScore(mId, mScore); +} + +void HGAssets::saveCurrentLocalProfile() +{ + return _impl->saveCurrentLocalProfile(); +} + +void HGAssets::saveAllProfiles() +{ + return _impl->saveAllProfiles(); +} + +bool HGAssets::anyLocalProfileActive() const +{ + return _impl->anyLocalProfileActive(); +} + +ProfileData& HGAssets::getCurrentLocalProfile() +{ + return _impl->getCurrentLocalProfile(); +} + +const ProfileData& HGAssets::getCurrentLocalProfile() const +{ + return _impl->getCurrentLocalProfile(); +} + +ProfileData* HGAssets::getLocalProfileByName(const std::string& mName) +{ + return _impl->getLocalProfileByName(mName); +} + +const ProfileData* HGAssets::getLocalProfileByName( + const std::string& mName) const +{ + return _impl->getLocalProfileByName(mName); +} + +std::size_t HGAssets::getLocalProfilesSize() +{ + return _impl->getLocalProfilesSize(); +} + +std::vector<std::string> HGAssets::getLocalProfileNames() +{ + return _impl->getLocalProfileNames(); +} + +bool HGAssets::pIsValidLocalProfile() const +{ + return _impl->pIsValidLocalProfile(); +} + +const std::string& HGAssets::pGetName() const +{ + return _impl->pGetName(); +} + +void HGAssets::pSaveCurrent() +{ + return _impl->pSaveCurrent(); +} + +void HGAssets::pSaveAll() +{ + return _impl->pSaveAll(); +} + +void HGAssets::pSetCurrent(const std::string& mName) +{ + return _impl->pSetCurrent(mName); +} + +void HGAssets::pCreate(const std::string& mName) +{ + return _impl->pCreate(mName); +} + +void HGAssets::pRemove(const std::string& mName) +{ + return _impl->pRemove(mName); +} + +sf::SoundBuffer* HGAssets::getSoundBuffer(const std::string& assetId) +{ + return _impl->getSoundBuffer(assetId); +} + +const std::string* HGAssets::getMusicPath(const std::string& assetId) const +{ + return _impl->getMusicPath(assetId); +} + +const std::unordered_map<std::string, LevelData>& +HGAssets::getLevelDatas() const noexcept +{ + return _impl->getLevelDatas(); +} + +const std::unordered_set<std::string>& +HGAssets::getPackIdsWithMissingDependencies() const noexcept +{ + return _impl->getPackIdsWithMissingDependencies(); +} + +void HGAssets::addLocalProfile(ProfileData&& profileData) +{ + return _impl->addLocalProfile(SSVOH_MOVE(profileData)); +} + +std::unordered_map<std::string, std::string>& HGAssets::getLuaFileCache() +{ + return _impl->getLuaFileCache(); +} + +const std::unordered_map<std::string, std::string>& +HGAssets::getLuaFileCache() const +{ + return _impl->getLuaFileCache(); +} + + } // namespace hg diff --git a/src/SSVOpenHexagon/Global/Config.cpp b/src/SSVOpenHexagon/Global/Config.cpp index f151320c..acb57fd2 100644 --- a/src/SSVOpenHexagon/Global/Config.cpp +++ b/src/SSVOpenHexagon/Global/Config.cpp @@ -161,7 +161,72 @@ defaultServerLevelWhitelist() "ndeltalevels_NDigger_delta_1_underworld_m_1", "ndeltalevels_NDigger_delta_1_underworld_m_1.001", "ndeltalevels_NDigger_delta_1_viper_m_1", - "ndeltalevels_NDigger_delta_1_viper_m_1.001" + "ndeltalevels_NDigger_delta_1_viper_m_1.001", + + // Hunter & others - Open Hexagon League 2 - Pack [Steam Workshop] + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Automaton_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Automaton_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Ezekiel_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Ezekiel_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Frostbite_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Frostbite_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Insight_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Insight_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Neurodance_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Neurodance_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Partygoer_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Partygoer_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Technicolor_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Technicolor_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Toxine_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Toxine_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Vault_m_1", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_Vault_m_1.01", + "ohl2pack_Hunter_&_Maniac_Open_Hexagon_League_2_1_VaultFixed_m_1.02", + + // Hunter & others - Open Hexagon League 3 - Pack [Steam Workshop] + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Blitz_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Blitz_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Bloodlines_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Bloodlines_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Cybereye_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Cybereye_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Cyclone_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Cyclone_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Cyclone_m_1.015", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Darkland_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Darkland_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Evirex_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Evirex_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Onyx_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Onyx_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Reactive_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Reactive_m_1.01", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Wilderness_m_1", + "ohl3pack_Hunter_&_Maniac_Open_Hexagon_League_3_1_Wilderness_m_1.01", + + // clang-format off + // Hunter & others - Open Hexagon League 3 - Pack [Steam Workshop] + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Catharsis_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Catharsis_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Heartbeat_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Heartbeat_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Joululumi_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Joululumi_m_1.001", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_JoululumiNormal_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_JoululumiNormal_m_1.001", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Killerbox_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Killerbox_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Luminara_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Luminara_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Sharpshooter_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Sharpshooter_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Signal_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_Signal_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_eclipse_m_1", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_eclipse_m_1.01", + "ohl4pack_Hunter_&_Fractal_&_zX_Open_Hexagon_League_4_1_teraflect_m_1" + // clang-format on // }; @@ -300,8 +365,12 @@ using cil = std::initializer_list<cmb>; X(showSwapParticles, bool, "show_swap_particles", true) \ X(playSwapReadySound, bool, "play_swap_ready_sound", true) \ X(showSwapBlinkingEffect, bool, "show_swap_blinking_effect", true) \ + X(useLuaFileCache, bool, "use_lua_file_cache", false) \ + X(disableGameRendering, bool, "disable_game_rendering", false) \ X_LINKEDVALUES_BINDS +// TODO: enable cache on server + namespace hg::Config { [[nodiscard]] static ssvuj::Obj& root() noexcept @@ -880,6 +949,16 @@ void setShowSwapBlinkingEffect(bool x) showSwapBlinkingEffect() = x; } +void setUseLuaFileCache(bool x) +{ + useLuaFileCache() = x; +} + +void setDisableGameRendering(bool x) +{ + disableGameRendering() = x; +} + [[nodiscard]] bool getOfficial() { return official(); @@ -1190,7 +1269,7 @@ void setShowSwapBlinkingEffect(bool x) return serverControlPort(); } -[[nodiscard]] const std::vector<std::string> getServerLevelWhitelist() +[[nodiscard]] const std::vector<std::string>& getServerLevelWhitelist() { return serverLevelWhitelist(); } @@ -1260,6 +1339,16 @@ void setShowSwapBlinkingEffect(bool x) return showSwapBlinkingEffect(); } +[[nodiscard]] bool getUseLuaFileCache() +{ + return useLuaFileCache(); +} + +[[nodiscard]] bool getDisableGameRendering() +{ + return disableGameRendering(); +} + //*********************************************************** // // KEYBOARD/MOUSE BINDS @@ -1476,43 +1565,42 @@ const std::array<JoystickTriggerGetter, []() -> unsigned int { return joystickAddToFavorites(); }, []() -> unsigned int { return joystickFavoritesMenu(); }}; -const std::string getJoystickBindNames(const Joystick::Jid bindID) +std::string getJoystickBindName(const Joystick::Jid bindID) { - static constexpr std::array<std::array<std::string_view, 2>, 12> - buttonsNames{{{"A", "SQUARE"}, {"B", "CROSS"}, {"X", "CIRCLE"}, - {"Y", "TRIANGLE"}, {"LB", "L1"}, {"RB", "R1"}, {"BACK", "L2"}, - {"START", "R2"}, {"LEFT STICK", "SELECT"}, {"RIGHT STICK", "START"}, + static std::array<std::array<std::string, 2>, 12> buttonsNames{ + {{"A", "SQUARE"}, {"B", "CROSS"}, {"X", "CIRCLE"}, {"Y", "TRIANGLE"}, + {"LB", "L1"}, {"RB", "R1"}, {"BACK", "L2"}, {"START", "R2"}, + {"LEFT STICK", "SELECT"}, {"RIGHT STICK", "START"}, {"LT", "LEFT STICK"}, {"RT", "RIGHT STICK"}}}; - std::string bindName; const unsigned int value{joystickTriggerGetters[toSizeT(bindID)]()}; if(value == 33) { - bindName = ""; + return ""; } - else + + constexpr unsigned int msVendorId{0x045E}; + constexpr unsigned int sonyVendorId{0x54C}; + + const unsigned int vendorId{ + sf::Joystick::isConnected(0) + ? sf::Joystick::getIdentification(0).vendorId + : 0}; + + using namespace std::string_literals; + + if(vendorId == msVendorId) { - constexpr unsigned int msVendorId{0x045E}; - constexpr unsigned int sonyVendorId{0x54C}; - const unsigned int vendorId{ - sf::Joystick::isConnected(0) - ? sf::Joystick::getIdentification(0).vendorId - : 0}; - - switch(vendorId) - { - case msVendorId: - bindName = value >= 12 ? "" : buttonsNames[value][0]; - break; - case sonyVendorId: - bindName = value >= 12 ? "" : buttonsNames[value][1]; - break; - default: bindName = ssvu::toStr(value); break; - } + return value >= 12 ? "" : buttonsNames[value][0]; + } + + if(vendorId == sonyVendorId) + { + return value >= 12 ? "" : buttonsNames[value][1]; } - return bindName; + return ssvu::toStr(value); } //********************************************** diff --git a/src/SSVOpenHexagon/Global/Instantiations.cpp b/src/SSVOpenHexagon/Global/Instantiations.cpp index 247d8044..7a210583 100644 --- a/src/SSVOpenHexagon/Global/Instantiations.cpp +++ b/src/SSVOpenHexagon/Global/Instantiations.cpp @@ -10,6 +10,7 @@ template class std::vector<std::string>; template class std::optional<int>; template class std::optional<std::size_t>; +template class std::optional<std::string>; template class std::unordered_map<std::string, float>; template class std::unordered_map<float, std::string>; diff --git a/src/SSVOpenHexagon/Online/Database.cpp b/src/SSVOpenHexagon/Online/Database.cpp index e319f3aa..57be81f0 100644 --- a/src/SSVOpenHexagon/Online/Database.cpp +++ b/src/SSVOpenHexagon/Online/Database.cpp @@ -41,31 +41,31 @@ inline auto makeStorage() { using namespace sqlite_orm; - auto storage = make_storage("ohdb.sqlite", // - // - make_table("users", // - make_column("id", &User::id, autoincrement(), primary_key()), // - make_column("steamId", &User::steamId, unique()), // - make_column("name", &User::name), // - make_column("passwordHash", &User::passwordHash) // - ), // - // - make_table("loginTokens", // - make_column( // - "id", &LoginToken::id, autoincrement(), primary_key()), // - make_column("userId", &LoginToken::userId, unique()), // - make_column("timestamp", &LoginToken::timestamp), // - make_column("token", &LoginToken::token) // - ), // - // - make_table("scores", // - make_column( // - "id", &Score::id, autoincrement(), primary_key()), // - make_column("levelValidator", &Score::levelValidator), // - make_column("timestamp", &Score::timestamp), // - make_column("userSteamId", &Score::userSteamId), // - make_column("value", &Score::value) // - ) // + auto storage = make_storage("ohdb.sqlite", // + // + make_table("users", // + make_column("id", &User::id, primary_key().autoincrement()), // + make_column("steamId", &User::steamId, unique()), // + make_column("name", &User::name), // + make_column("passwordHash", &User::passwordHash) // + ), // + // + make_table("loginTokens", // + make_column( // + "id", &LoginToken::id, primary_key().autoincrement()), // + make_column("userId", &LoginToken::userId, unique()), // + make_column("timestamp", &LoginToken::timestamp), // + make_column("token", &LoginToken::token) // + ), // + // + make_table("scores", // + make_column( // + "id", &Score::id, primary_key().autoincrement()), // + make_column("levelValidator", &Score::levelValidator), // + make_column("timestamp", &Score::timestamp), // + make_column("userSteamId", &Score::userSteamId), // + make_column("value", &Score::value) // + ) // // ); @@ -252,7 +252,7 @@ void removeAllStaleLoginTokens() std::uint32_t index = 0; for(const auto& row : query) { - result.push_back( // + result.push_back( // ProcessedScore{ .position = index, // .userName = std::get<0>(row), // diff --git a/src/SSVOpenHexagon/Utils/LuaMetadataProxy.cpp b/src/SSVOpenHexagon/Utils/LuaMetadataProxy.cpp index ea2b07f4..82c7f265 100644 --- a/src/SSVOpenHexagon/Utils/LuaMetadataProxy.cpp +++ b/src/SSVOpenHexagon/Utils/LuaMetadataProxy.cpp @@ -174,7 +174,8 @@ catch(...) ssvu::lo("LuaMetadataProxy") << "Failed to generate documentation\n"; } #else -{} +{ +} #endif LuaMetadataProxy& LuaMetadataProxy::arg( diff --git a/src/SSVOpenHexagon/Utils/LuaWrapper.cpp b/src/SSVOpenHexagon/Utils/LuaWrapper.cpp index 7eb37437..ccbb1111 100644 --- a/src/SSVOpenHexagon/Utils/LuaWrapper.cpp +++ b/src/SSVOpenHexagon/Utils/LuaWrapper.cpp @@ -53,12 +53,12 @@ LuaContext::LuaContext(bool openDefaultLibs) } } -LuaContext::LuaContext(LuaContext&& s) : _state(s._state) +LuaContext::LuaContext(LuaContext&& s) noexcept : _state(s._state) { s._state = nullptr; } -LuaContext& LuaContext::operator=(LuaContext&& s) +LuaContext& LuaContext::operator=(LuaContext&& s) noexcept { std::swap(_state, s._state); return *this; @@ -93,13 +93,13 @@ LuaContext::WrongTypeException::WrongTypeException() : std::runtime_error("Trying to cast a lua variable to an invalid type") {} -void LuaContext::_getGlobal(const std::string& mVarName) const +void LuaContext::_getGlobal(std::string_view mVarName) const { // first a little optimization: if mVarName contains no dot, we can // directly call lua_getglobal if(std::find(mVarName.begin(), mVarName.end(), '.') == mVarName.end()) { - lua_getglobal(_state, mVarName.c_str()); + lua_getglobal(_state, mVarName.data()); return; } @@ -139,7 +139,7 @@ void LuaContext::_getGlobal(const std::string& mVarName) const if(!lua_istable(_state, -1)) { lua_pop(_state, 1); - throw VariableDoesntExistException(mVarName); + throw VariableDoesntExistException(std::string{mVarName}); } // replacing the current table in the stack by its member @@ -156,7 +156,7 @@ void LuaContext::_getGlobal(const std::string& mVarName) const if(lua_isnil(_state, -1)) { lua_pop(_state, 1); - throw VariableDoesntExistException(mVarName); + throw VariableDoesntExistException(std::string{mVarName}); } currentVar = nextVar; // updating currentVar @@ -164,62 +164,61 @@ void LuaContext::_getGlobal(const std::string& mVarName) const while(nextVar != mVarName.end()); } -void LuaContext::_setGlobal(const std::string& mVarName) +void LuaContext::_setGlobal(std::string_view mVarName) +try { - try + SSVOH_ASSERT(lua_gettop(_state) >= 1); // making sure there's + // something on the stack + // (ie. the value to set) + + // two possibilities: either "variable" is a global variable, or + // a member of an array + std::size_t lastDot = mVarName.find_last_of('.'); + + if(lastDot == std::string::npos) { - SSVOH_ASSERT(lua_gettop(_state) >= 1); // making sure there's - // something on the stack - // (ie. the value to set) + // this is the first case, we simply call setglobal (which + // cleans the stack) + lua_setglobal(_state, mVarName.data()); + return; + } + + std::string varNameAsStr{mVarName}; // needed for null-terminated substrs + const auto tableName = varNameAsStr.substr(0, lastDot); - // two possibilities: either "variable" is a global variable, or - // a member of an array - std::size_t lastDot = mVarName.find_last_of('.'); + // in the second case, we call _getGlobal on the table name + _getGlobal(tableName); - if(lastDot == std::string::npos) + try + { + if(!lua_istable(_state, -1)) { - // this is the first case, we simply call setglobal (which - // cleans the stack) - lua_setglobal(_state, mVarName.c_str()); + throw VariableDoesntExistException(varNameAsStr); } - else - { - const auto tableName = mVarName.substr(0, lastDot); - - // in the second case, we call _getGlobal on the table name - _getGlobal(tableName); - try - { - if(!lua_istable(_state, -1)) - { - throw VariableDoesntExistException(mVarName); - } - - // now we have our value at -2 (was pushed before - // _setGlobal is called) and our table at -1 - lua_pushstring(_state, - mVarName.substr(lastDot + 1).c_str()); // value at -3, - // table at -2, - // key at -1 - lua_pushvalue(_state, -3); // value at -4, table at -3, - // key at -2, value at -1 - lua_settable(_state, -3); // value at -2, table at -1 - lua_pop(_state, 2); // stack empty \o/ - } - catch(...) - { - lua_pop(_state, 2); - throw; - } - } + // now we have our value at -2 (was pushed before + // _setGlobal is called) and our table at -1 + lua_pushstring(_state, + varNameAsStr.substr(lastDot + 1).c_str()); // value at -3, + // table at -2, + // key at -1 + lua_pushvalue(_state, -3); // value at -4, table at -3, + // key at -2, value at -1 + lua_settable(_state, -3); // value at -2, table at -1 + lua_pop(_state, 2); // stack empty \o/ } catch(...) { - lua_pop(_state, 1); + lua_pop(_state, 2); throw; } } +catch(...) +{ + lua_pop(_state, 1); + throw; +} + void LuaContext::_load(std::istream& code) { @@ -262,7 +261,6 @@ void LuaContext::_load(std::istream& code) // we create an instance of Reader, and we call lua_load Reader reader(code); - const int loadReturnValue = lua_load(_state, &Reader::read, &reader, "chunk"); @@ -284,46 +282,10 @@ void LuaContext::_load(std::istream& code) } } -void LuaContext::_load(const std::string& code) +void LuaContext::_load(std::string_view code) { - // since the lua_load function requires a static function, we use - // this structure - // the Reader structure is at the same time an object storing an - // std::istream and a buffer, - // and a static function provider - struct SReader - { - const std::string& str; - bool consumed; - - SReader(const std::string& s) : str(s), consumed{false} - {} - - // read function; "data" must be an instance of Reader - static const char* read(lua_State*, void* data, std::size_t* size) - { - SSVOH_ASSERT(size != nullptr); - SSVOH_ASSERT(data != nullptr); - - SReader& me = *((SReader*)data); - - if(me.consumed || me.str.empty()) - { - *size = 0; - return nullptr; - } - - me.consumed = true; - - *size = me.str.size(); - return me.str.data(); - } - }; - - // we create an instance of Reader, and we call lua_load - SReader sReader(code); const int loadReturnValue = - lua_load(_state, &SReader::read, &sReader, "chunk"); + luaL_loadbuffer(_state, code.data(), code.size(), "chunk"); // now we have to check return value if(loadReturnValue != 0) diff --git a/src/SSVOpenHexagon/Utils/Utils.cpp b/src/SSVOpenHexagon/Utils/Utils.cpp index 3e9d3ae8..3d447d06 100644 --- a/src/SSVOpenHexagon/Utils/Utils.cpp +++ b/src/SSVOpenHexagon/Utils/Utils.cpp @@ -19,32 +19,62 @@ #include <string> #include <fstream> #include <stdexcept> +#include <unordered_map> namespace hg::Utils { void runLuaCode(Lua::LuaContext& mLua, const std::string& mCode) +try { - try - { - mLua.executeCode(mCode); - } - catch(std::runtime_error& mError) - { - ssvu::lo("hg::Utils::runLuaCode") << "Fatal Lua error\n" - << "Code: " << mCode << '\n' - << "Error: " << mError.what() << '\n' - << std::endl; + mLua.executeCode(mCode); +} +catch(std::runtime_error& mError) +{ + ssvu::lo("hg::Utils::runLuaCode") << "Fatal Lua error\n" + << "Code: " << mCode << '\n' + << "Error: " << mError.what() << '\n' + << std::endl; - throw; - } - catch(...) + throw; +} +catch(...) +{ + ssvu::lo("hg::Utils::runLuaCode") << "Fatal unknown Lua error\n" + << "Code: " << mCode << '\n' + << std::endl; + + throw; +} + +bool runLuaFileCached( + HGAssets& assets, Lua::LuaContext& mLua, const std::string& mFileName) +{ + std::unordered_map<std::string, std::string>& cache = + assets.getLuaFileCache(); + + static std::string buffer; + + auto it = cache.find(mFileName); + const bool found = it != cache.end(); + + if(!found) { - ssvu::lo("hg::Utils::runLuaCode") << "Fatal unknown Lua error\n" - << "Code: " << mCode << '\n' - << std::endl; + std::ifstream t(mFileName, std::ios::binary | std::ios::in); - throw; + t.seekg(0, std::ios::end); + const std::streamsize size = t.tellg(); + buffer.resize(size); + + t.seekg(0, std::ios::beg); + t.read(buffer.data(), size); + + auto res = cache.emplace(mFileName, std::move(buffer)); + SSVOH_ASSERT(res.second); + it = res.first; } + + runLuaCode(mLua, it->second); + return found; } void runLuaFile(Lua::LuaContext& mLua, const std::string& mFileName) diff --git a/test/ReplayExecution.t.cpp b/test/ReplayExecution.t.cpp index 84e802fc..08c7b1df 100644 --- a/test/ReplayExecution.t.cpp +++ b/test/ReplayExecution.t.cpp @@ -2,9 +2,12 @@ // License: Academic Free License ("AFL") v. 3.0 // AFL License page: https://opensource.org/licenses/AFL-3.0 +#include "SSVOpenHexagon/Data/ProfileData.hpp" + #include "SSVOpenHexagon/Global/Assets.hpp" #include "SSVOpenHexagon/Global/Config.hpp" #include "SSVOpenHexagon/Global/Version.hpp" + #include "SSVOpenHexagon/Core/HexagonGame.hpp" #include "TestUtils.hpp" @@ -62,9 +65,7 @@ try std::optional<hg::replay_file> rf; hg.onDeathReplayCreated = [&](const hg::replay_file& newRf) - { - rf.emplace(newRf); - }; + { rf.emplace(newRf); }; hg.newGame(packs[i % packs.size()], levels[i % levels.size()], true /* firstPlay */, 1.f /* diffMult */, diff --git a/test/ReplayExecutionBenchmark.t.cpp b/test/ReplayExecutionBenchmark.t.cpp new file mode 100644 index 00000000..9f7f29bf --- /dev/null +++ b/test/ReplayExecutionBenchmark.t.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2013-2020 Vittorio Romeo +// License: Academic Free License ("AFL") v. 3.0 +// AFL License page: https://opensource.org/licenses/AFL-3.0 + +#include "SSVOpenHexagon/Data/ProfileData.hpp" + +#include "SSVOpenHexagon/Global/Assets.hpp" +#include "SSVOpenHexagon/Global/Config.hpp" +#include "SSVOpenHexagon/Global/Version.hpp" + +#include "SSVOpenHexagon/Core/HexagonGame.hpp" + +#include "TestUtils.hpp" + +#include <array> +#include <optional> +#include <random> +#include <stdexcept> + +int main() +try +{ + constexpr std::array packs{ + "ohvrvanilla_vittorio_romeo_cube_1", // + "ohvrvanilla_vittorio_romeo_experimental_1", // + "ohvrvanilla_vittorio_romeo_orthoplex_1", // + "ohvrvanilla_vittorio_romeo_orthoplex_1" // + }; + + constexpr std::array levels{ + "ohvrvanilla_vittorio_romeo_cube_1_apeirogon", // + "ohvrvanilla_vittorio_romeo_experimental_1_autotest0", // + "ohvrvanilla_vittorio_romeo_orthoplex_1_arcadia", // + "ohvrvanilla_vittorio_romeo_orthoplex_1_bipolarity" // + }; + + hg::Config::loadConfig({}); + + hg::HGAssets assets{nullptr /* steamManager */, true /* headless */}; + + hg::ProfileData fakeProfile{hg::GAME_VERSION, "testProfile", {}, {}}; + assets.addLocalProfile(std::move(fakeProfile)); + assets.pSetCurrent("testProfile"); + + const auto doTest = [&](int i, bool differentHG, ssvs::GameWindow* gw) + { + hg::HexagonGame hg{ + nullptr /* steamManager */, // + nullptr /* discordManager */, // + assets, // + nullptr /* audio */, // + gw, // + nullptr /* client */ // + }; + + if(getRndBool()) + { + hg.executeRandomInputs = true; + } + else + { + hg.alwaysSpinRight = true; + } + + std::optional<hg::replay_file> rf; + + hg.onDeathReplayCreated = [&](const hg::replay_file& newRf) + { rf.emplace(newRf); }; + + hg.newGame(packs[i % packs.size()], levels[i % levels.size()], + true /* firstPlay */, 1.f /* diffMult */, + /* mExecuteLastReplay */ false); + + hg.setMustStart(true); + const double score = + hg.executeGameUntilDeath( + 1 /* maxProcessingSeconds */, 1.f /* timescale */) + .value() + .playedTimeSeconds; + + TEST_ASSERT(rf.has_value()); + + std::optional<hg::HexagonGame::GameExecutionResult> score2; + if(differentHG) + { + hg::HexagonGame hg2{ + nullptr /* steamManager */, // + nullptr /* discordManager */, // + assets, // + nullptr /* audio */, // + nullptr /* window */, // + nullptr /* client */ // + }; + + score2 = hg2.runReplayUntilDeathAndGetScore( + rf.value(), 1 /* maxProcessingSeconds */, 1.f /* timescale */); + } + else + { + score2 = hg.runReplayUntilDeathAndGetScore( + rf.value(), 1 /* maxProcessingSeconds */, 1.f /* timescale */); + } + + TEST_ASSERT(score2.has_value()); + const double replayPlayedTimeSeconds = score2.value().playedTimeSeconds; + + // std::cerr << score << " == " << replayPlayedTimeSeconds << std::endl; + + TEST_ASSERT_EQ(score, replayPlayedTimeSeconds); + }; + + for(int i = 0; i < 100; ++i) + { + doTest(i, false, nullptr); + doTest(i, true, nullptr); + } + + return 0; +} +catch(const std::runtime_error& e) +{ + std::cerr << "EXCEPTION: " << e.what() << std::endl; +} +catch(...) +{ + std::cerr << "EXCEPTION: unknown" << std::endl; +} diff --git a/test/TestUtils.hpp b/test/TestUtils.hpp index 99fbe484..2702e291 100644 --- a/test/TestUtils.hpp +++ b/test/TestUtils.hpp @@ -17,8 +17,6 @@ #define SA_TYPE(value, type) \ static_assert(::std::is_same_v<decltype value, TEST_IMPL_DEPARENS type>) -#define VRM_CORE_LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1) - namespace test_impl::impl { inline auto& get_ostringstream() noexcept @@ -81,7 +79,10 @@ void output_expected(TStream& s, const char* expected, const T& rhs_result) template <typename TF> void do_test(bool x, TF&& f) { - if(VRM_CORE_LIKELY(x)) return; + if(x) [[likely]] + { + return; + } auto& error(impl::clear_and_get_ostringstream()); f(error); diff --git a/vbox/build_and_upload_server.sh b/vbox/build_and_upload_server.sh index 15e7b82b..dfdc8633 100755 --- a/vbox/build_and_upload_server.sh +++ b/vbox/build_and_upload_server.sh @@ -18,18 +18,12 @@ echo "Done" && \ \ echo "Uploading build binary to server..." && \ scp "$OH_VBOX_ROOT/_RELEASE/SSVOpenHexagonVbox" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/_RELEASE/SSVOpenHexagonVbox" && \ -scp "$OH_VBOX_ROOT/buildlx/_deps/sfml-build/lib/libsfml-audio.so.3.0" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/sfml-build/lib/libsfml-audio.so.3.0" && \ -scp "$OH_VBOX_ROOT/buildlx/_deps/sfml-build/lib/libsfml-network.so.3.0" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/sfml-build/lib/libsfml-network.so.3.0" && \ scp "$OH_VBOX_ROOT/buildlx/_deps/luajit-build/src/libluajit.so" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/luajit-build/src/libluajit.so" && \ scp "$OH_VBOX_ROOT/buildlx/_deps/zlib-build/libz.so.1" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/zlib-build/libz.so.1" && \ scp "$OH_VBOX_ROOT/buildlx/_deps/libsodium-cmake-build/libsodium.so" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/libsodium-cmake-build/libsodium.so" && \ scp "$OH_VBOX_ROOT/buildlx/_deps/imgui-sfml-build/libImGui-SFML.so" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/imgui-sfml-build/libImGui-SFML.so" && \ -scp "$OH_VBOX_ROOT/buildlx/_deps/sfml-build/lib/libsfml-graphics.so.3.0" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/sfml-build/lib/libsfml-graphics.so.3.0" && \ -scp "$OH_VBOX_ROOT/buildlx/_deps/sfml-build/lib/libsfml-window.so.3.0" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/sfml-build/lib/libsfml-window.so.3.0" && \ -scp "$OH_VBOX_ROOT/buildlx/_deps/sfml-build/lib/libsfml-system.so.3.0" "vittorioromeo@139.162.199.162:/home/vittorioromeo/OHWorkspace/SSVOpenHexagon/buildlx/_deps/sfml-build/lib/libsfml-system.so.3.0" && \ echo "Done" && \ \ echo "Running 'update_server.sh' on server..." && \ ssh vittorioromeo@139.162.199.162 -f "cd $OH_SERVER_ROOT/server && ./update_server.sh" && \ echo "Done" - |