From 59174e76af2d3b90e102df143fc358a4d933e2df Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sat, 5 Jan 2019 05:48:51 -0500 Subject: [PATCH] Update patrons Reduce compile-times by removing lambda shenanigans and moving data that is already serialized into runtime systems to runtime Fix bugs with precedence ordering related to index and new_index Prepare to add 20000 more if constexpr to reduce compile-times even further. --- CONTRIBUTORS.md | 26 +- cmake/Modules/FindLua.cmake | 233 -- cmake/Modules/FindLuaBuild/LuaVanilla.cmake | 94 +- include/sol/call.hpp | 2 +- include/sol/compatibility/version.hpp | 71 +- include/sol/demangle.hpp | 14 +- include/sol/error_handler.hpp | 2 +- include/sol/forward.hpp | 8 + include/sol/metatable.hpp | 48 +- include/sol/optional.hpp | 10 +- include/sol/optional_implementation.hpp | 3022 +++++++++++------ include/sol/protected_function.hpp | 1 + include/sol/sol.hpp | 14 +- include/sol/stack.hpp | 25 +- include/sol/stack_check_unqualified.hpp | 3 +- include/sol/stack_core.hpp | 47 +- include/sol/stack_get_unqualified.hpp | 3 +- include/sol/stack_probe.hpp | 6 +- include/sol/stack_push.hpp | 31 +- include/sol/string_view.hpp | 20 +- include/sol/table_core.hpp | 52 +- include/sol/types.hpp | 11 +- include/sol/usertype_storage.hpp | 481 +-- .../runtime_tests/source/usertype_runtime.cpp | 64 + 24 files changed, 2667 insertions(+), 1621 deletions(-) delete mode 100644 cmake/Modules/FindLua.cmake diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b8f2f177..8793f37c 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,18 +1,26 @@ -# Donators! ♥ +# 🎉 Donators! ♥ 🎉 Thank you to all patrons, donators and contributors who help keep sol2 amazing. -Robert Salvet -Ορφέας Ζαφείρης - 2x Donations! -Michael Wallar -Johannes Schultz -Dailidzionak Ilya +- Robert Salvet +- Ορφέας Ζαφείρης - 2x Donations! +- Michael Wallar +- Johannes Schultz +- Dailidzionak Ilya +- BECKMANN & EGLE Industrieelektronik GmbH [bue.de](https://www.bue.de/) -# Patrons +# 🎉 Patrons! ♥ 🎉 Beyond just a one-time donation, patrons make a continued commitment to help keep sol2 supported and bug-free. Thank you for your patronage! Here are the supporters that wanted to be featured as sol2 contributors. -Michael Caisse -Joshua Fisher +- Michael Caisse +- Joshua Fisher +- Ορφέας Ζαφείρης + +# Company Patrons / Supporters # + +Companies who sign up for a long-term support contract or patronage are listed here! They really push forward what's possible with sol2 (and the newer v3)! Please reach out to phdofthehouse@gmail.com if you are interested in a custom solution or a long-term support contract that goes beyond the current release's needs! + +- Intrepid Control Systems [intrepidcs.com](https://www.intrepidcs.com/) diff --git a/cmake/Modules/FindLua.cmake b/cmake/Modules/FindLua.cmake deleted file mode 100644 index 1e498a86..00000000 --- a/cmake/Modules/FindLua.cmake +++ /dev/null @@ -1,233 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#.rst: -# FindLua -# ------- -# -# -# -# Locate Lua library. This module defines -# -# :: -# -# LUA_FOUND - if false, do not try to link to Lua -# LUA_LIBRARIES - both lua and lualib -# LUA_INCLUDE_DIR - where to find lua.h -# LUA_LIBRARY_DIR - Dir(s) where Lua libraries are found -# LUA_VERSION_STRING - the version of Lua found -# LUA_VERSION_MAJOR - the major version of Lua -# LUA_VERSION_MINOR - the minor version of Lua -# LUA_VERSION_PATCH - the patch version of Lua -# -# -# -# Note that the expected include convention is -# -# :: -# -# #include "lua.h" -# -# and not -# -# :: -# -# #include -# -# This is because, the lua location is not standardized and may exist in -# locations other than lua/ - -unset(_lua_include_subdirs) -unset(_lua_append_versions) -unset(_lua_library_names) - - -include(${CMAKE_CURRENT_LIST_DIR}/FindLua/set_version_vars.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/FindLua/version_check.cmake) - -# # this is a function only to have all the variables inside go away automatically -# function(_lua_set_version_vars) -# set(LUA_VERSIONS5 5.3 5.2 5.1 5.0) - -# if (Lua_FIND_VERSION_EXACT) -# if (Lua_FIND_VERSION_COUNT GREATER 1) -# set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}) -# endif () -# elseif (Lua_FIND_VERSION) -# # once there is a different major version supported this should become a loop -# if (NOT Lua_FIND_VERSION_MAJOR GREATER 5) -# if (Lua_FIND_VERSION_COUNT EQUAL 1) -# set(_lua_append_versions ${LUA_VERSIONS5}) -# else () -# foreach (subver IN LISTS LUA_VERSIONS5) -# if (NOT subver VERSION_LESS ${Lua_FIND_VERSION}) -# list(APPEND _lua_append_versions ${subver}) -# endif () -# endforeach () -# endif () -# endif () -# else () -# # once there is a different major version supported this should become a loop -# set(_lua_append_versions ${LUA_VERSIONS5}) -# endif () - -# list(APPEND _lua_include_subdirs "include/lua" "include") - -# foreach (ver IN LISTS _lua_append_versions) -# string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}") -# list(APPEND _lua_include_subdirs -# include/lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} -# include/lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} -# include/lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} -# ) -# list(APPEND _lua_library_names -# lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} -# lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} -# lua.${CMAKE_MATCH_1}.${CMAKE_MATCH_2} -# lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} -# ) -# endforeach () - -# set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE) -# set(_lua_append_versions "${_lua_append_versions}" PARENT_SCOPE) -# endfunction(_lua_set_version_vars) - -# function(_lua_check_header_version _hdr_file) -# # At least 5.[012] have different ways to express the version -# # so all of them need to be tested. Lua 5.2 defines LUA_VERSION -# # and LUA_RELEASE as joined by the C preprocessor, so avoid those. -# file(STRINGS "${_hdr_file}" lua_version_strings -# REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*") - -# string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};") -# if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$") -# string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};") -# string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};") -# set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}") -# else () -# string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") -# if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$") -# string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") -# endif () -# string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}") -# string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}") -# string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}") -# endif () -# foreach (ver IN LISTS _lua_append_versions) -# if (ver STREQUAL "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}") -# set(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR} PARENT_SCOPE) -# set(LUA_VERSION_MINOR ${LUA_VERSION_MINOR} PARENT_SCOPE) -# set(LUA_VERSION_PATCH ${LUA_VERSION_PATCH} PARENT_SCOPE) -# set(LUA_VERSION_STRING ${LUA_VERSION_STRING} PARENT_SCOPE) -# return() -# endif () -# endforeach () -# endfunction(_lua_check_header_version) - -_lua_set_version_vars(lua "") - -find_path(LUA_INCLUDE_DIR lua.h - HINTS - ENV LUA_DIR - PATH_SUFFIXES ${_lua_include_subdirs} include - PATHS - ${LUA_DIR} - ~/Library/Frameworks - /Library/Frameworks - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt - /usr - /usr/local # Homebrew -) - -if (LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") - _lua_check_header_version("${LUA_INCLUDE_DIR}/lua.h" "LUA") -endif () - -if (NOT LUA_VERSION_STRING) - foreach (subdir IN LISTS _lua_include_subdirs) - unset(LUA_INCLUDE_PREFIX CACHE) - find_path(LUA_INCLUDE_PREFIX ${subdir}/lua.h - HINTS - ENV LUA_DIR - PATHS - ${LUA_DIR} - ~/Library/Frameworks - /Library/Frameworks - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt - /usr - /usr/local - ) - if (LUA_INCLUDE_PREFIX) - _lua_check_header_version("${LUA_INCLUDE_PREFIX}/${subdir}/lua.h") - if (LUA_VERSION_STRING) - set(LUA_INCLUDE_DIR "${LUA_INCLUDE_PREFIX}/${subdir}") - break() - endif () - endif () - endforeach () -endif () -unset(_lua_include_subdirs) -unset(_lua_append_versions) - -find_library(LUA_LIBRARY - NAMES ${_lua_library_names} lua - HINTS - ENV LUA_DIR - PATH_SUFFIXES lib bin - PATHS - ${LUA_DIR} - ~/Library/Frameworks - /Library/Frameworks - /sw - /opt/local - /opt/csw - /opt - /usr - /usr/local - - # From the include_dir - ${LUA_INCLUDE_DIR}/../lib -) -unset(_lua_library_names) - -if (LUA_LIBRARY) - # include the math library for Unix - if (UNIX AND NOT APPLE AND NOT BEOS) - find_library(LUA_MATH_LIBRARY m) - set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}") - - # include dl library for statically-linked Lua library - get_filename_component(LUA_LIB_EXT ${LUA_LIBRARY} EXT) - if (LUA_LIB_EXT STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX) - list(APPEND LUA_LIBRARIES ${CMAKE_DL_LIBS}) - endif () - - # For Windows and Mac, don't need to explicitly include the math library - else () - set(LUA_LIBRARIES "${LUA_LIBRARY}") - endif () - - set(LUA_LIBRARY_DIR ) - foreach (lib ${LUA_LIBRARIES}) - get_filename_component(lib_dir ${lib} DIRECTORY CACHE) - list(APPEND LUA_LIBRARY_DIR ${lib_dir}) - endforeach () - list(REMOVE_DUPLICATES LUA_LIBRARY_DIR) -endif () - -include(FindPackageHandleStandardArgs) - -# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if -# all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua - FOUND_VAR Lua_FOUND - REQUIRED_VARS LUA_LIBRARIES LUA_LIBRARY_DIR LUA_INCLUDE_DIR - VERSION_VAR LUA_VERSION_STRING) - -mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY LUA_LIBRARY_DIR LUA_MATH_LIBRARY) diff --git a/cmake/Modules/FindLuaBuild/LuaVanilla.cmake b/cmake/Modules/FindLuaBuild/LuaVanilla.cmake index fef51896..c0a0a9a5 100644 --- a/cmake/Modules/FindLuaBuild/LuaVanilla.cmake +++ b/cmake/Modules/FindLuaBuild/LuaVanilla.cmake @@ -236,32 +236,38 @@ prepend(LUA_VANILLA_LUA_SOURCES "${LUA_VANILLA_SOURCE_DIR}/" ${LUA_VANILLA_LUA_S prepend(LUA_VANILLA_LUAC_SOURCES "${LUA_VANILLA_SOURCE_DIR}/" ${LUA_VANILLA_LUAC_SOURCES}) # download, just for the sake of download + extract -# have to use 2 different commands just to have an empty command -# that results in nothing being run -# TODO: talk to smarter CMake people...? +# or pull from local folder +if (LUA_LOCAL_DIR) + file(COPY "${LUA_LOCAL_DIR}/src" + DESTINATION "${LUA_BUILD_TOPLEVEL}") + file(COPY "${LUA_LOCAL_DIR}/include" + DESTINATION "${LUA_BUILD_TOPLEVEL}") + add_custom_target(LUA_VANILLA + DEPENDS "${LUA_VANILLA_LIB_SOURCES}" "${LUA_VANILLA_LUA_SOURCES}" "${LUA_VANILLA_LUAC_SOURCES}") + set(LUA_VANILLA_INCLUDE_DIRS ${LUA_VANILLA_INCLUDE_DIRS} "${LUA_VANILLA_SOURCE_DIR}" "${LUA_BUILD_TOPLEVEL}/include") +else() + ExternalProject_Add(LUA_VANILLA + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + TLS_VERIFY TRUE + PREFIX ${LUA_BUILD_TOPLEVEL} + SOURCE_DIR ${LUA_BUILD_TOPLEVEL} + DOWNLOAD_DIR ${LUA_BUILD_TOPLEVEL} + TMP_DIR "${LUA_BUILD_TOPLEVEL}-tmp" + STAMP_DIR "${LUA_BUILD_TOPLEVEL}-stamp" + INSTALL_DIR "${LUA_BUILD_INSTALL_DIR}" + URL ${LUA_VANILLA_DOWNLOAD_URL} + URL_MD5 ${LUA_VANILLA_MD5} + URL_HASH SHA1=${LUA_VANILLA_SHA1} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + BUILD_BYPRODUCTS "${LUA_VANILLA_LIB_SOURCES}" "${LUA_VANILLA_LUA_SOURCES}" "${LUA_VANILLA_LUAC_SOURCES}") -ExternalProject_Add(LUA_VANILLA - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - TLS_VERIFY TRUE - PREFIX ${LUA_BUILD_TOPLEVEL} - SOURCE_DIR ${LUA_BUILD_TOPLEVEL} - DOWNLOAD_DIR ${LUA_BUILD_TOPLEVEL} - TMP_DIR "${LUA_BUILD_TOPLEVEL}-tmp" - STAMP_DIR "${LUA_BUILD_TOPLEVEL}-stamp" - INSTALL_DIR "${LUA_BUILD_INSTALL_DIR}" - URL ${LUA_VANILLA_DOWNLOAD_URL} - URL_MD5 ${LUA_VANILLA_MD5} - URL_HASH SHA1=${LUA_VANILLA_SHA1} - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - BUILD_BYPRODUCTS "${LUA_VANILLA_LIB_SOURCES}" "${LUA_VANILLA_LUA_SOURCES}" "${LUA_VANILLA_LUAC_SOURCES}") - -# make a quick lua.hpp for 5.1 targets that don't have it -if (LUA_VANILLA_GENERATE_LUA_HPP) - set(LUA_VANILLA_LUA_HPP_CONTENT "// lua.hpp + # make a quick lua.hpp for 5.1 targets that don't have it + if (LUA_VANILLA_GENERATE_LUA_HPP) + set(LUA_VANILLA_LUA_HPP_CONTENT "// lua.hpp // Lua header files for C++ // <> not supplied automatically because Lua also compiles as C++ @@ -271,21 +277,24 @@ extern \"C\" { #include \"lauxlib.h\" } ") - set(LUA_VANILLA_SOURCE_LUA_HPP "${LUA_BUILD_TOPLEVEL}-tmp/lua.hpp") - set(LUA_VANILLA_DESTINATION_LUA_HPP "${LUA_VANILLA_SOURCE_DIR}/lua.hpp") - file(WRITE "${LUA_VANILLA_SOURCE_LUA_HPP}" "${LUA_VANILLA_LUA_HPP_CONTENT}") - file(TO_NATIVE_PATH "${LUA_VANILLA_SOURCE_LUA_HPP}" LUA_VANILLA_SOURCE_LUA_HPP) - file(TO_NATIVE_PATH "${LUA_VANILLA_DESTINATION_LUA_HPP}" LUA_VANILLA_DESTINATION_LUA_HPP) - ExternalProject_Add_Step(LUA_VANILLA - prebuild - # after download, before build - DEPENDEES download - DEPENDERS build - BYPRODUCTS "${LUA_VANILLA_DESTINATION_LUA_HPP}" - COMMENT "Moving \"${LUA_VANILLA_SOURCE_LUA_HPP}\" to \"${LUA_VANILLA_DESTINATION_LUA_HPP}\"..." - COMMAND "${CMAKE_COMMAND}" -E copy "${LUA_VANILLA_SOURCE_LUA_HPP}" "${LUA_VANILLA_DESTINATION_LUA_HPP}") -endif() + set(LUA_VANILLA_SOURCE_LUA_HPP "${LUA_BUILD_TOPLEVEL}-tmp/lua.hpp") + set(LUA_VANILLA_DESTINATION_LUA_HPP "${LUA_VANILLA_SOURCE_DIR}/lua.hpp") + file(WRITE "${LUA_VANILLA_SOURCE_LUA_HPP}" "${LUA_VANILLA_LUA_HPP_CONTENT}") + file(TO_NATIVE_PATH "${LUA_VANILLA_SOURCE_LUA_HPP}" LUA_VANILLA_SOURCE_LUA_HPP) + file(TO_NATIVE_PATH "${LUA_VANILLA_DESTINATION_LUA_HPP}" LUA_VANILLA_DESTINATION_LUA_HPP) + ExternalProject_Add_Step(LUA_VANILLA + prebuild + # after download, before build + DEPENDEES download + DEPENDERS build + BYPRODUCTS "${LUA_VANILLA_DESTINATION_LUA_HPP}" + COMMENT "Moving \"${LUA_VANILLA_SOURCE_LUA_HPP}\" to \"${LUA_VANILLA_DESTINATION_LUA_HPP}\"..." + COMMAND "${CMAKE_COMMAND}" -E copy "${LUA_VANILLA_SOURCE_LUA_HPP}" "${LUA_VANILLA_DESTINATION_LUA_HPP}") + endif() + set(LUA_VANILLA_INCLUDE_DIRS ${LUA_VANILLA_SOURCE_DIR}) +endif() +message(STATUS "${LUA_VANILLA_INCLUDE_DIRS}") # # Target names set(liblua "liblua-${LUA_VANILLA_VERSION}") set(luainterpreter "lua-${LUA_VANILLA_VERSION}") @@ -308,13 +317,12 @@ set_target_properties(${liblua} C_STANDARD 99 C_EXTENSIONS TRUE POSITION_INDEPENDENT_CODE TRUE - INCLUDE_DIRECTORIES ${LUA_VANILLA_SOURCE_DIR} OUTPUT_NAME ${LUA_BUILD_LIBNAME} RUNTIME_OUTPUT_NAME ${LUA_BUILD_LIBNAME} LIBRARY_OUTPUT_NAME ${LUA_BUILD_LIBNAME} ARCHIVE_OUTPUT_NAME ${LUA_BUILD_LIBNAME}) target_include_directories(${liblua} - PUBLIC ${LUA_VANILLA_SOURCE_DIR}) + PUBLIC "${LUA_VANILLA_INCLUDE_DIRS}") target_compile_definitions(${liblua} PUBLIC LUA_COMPAT_ALL ${LUA_VANILLA_DLL_DEFINE}) if (MSVC) @@ -352,7 +360,7 @@ set_target_properties(${luainterpreter} C_EXTENSIONS TRUE OUTPUT_NAME lua-${LUA_VANILLA_VERSION}) target_include_directories(${luainterpreter} - PRIVATE ${LUA_VANILLA_SOURCE_DIR}) + PRIVATE "${LUA_VANILLA_INCLUDE_DIRS}") target_compile_definitions(${luainterpreter} PUBLIC LUA_COMPAT_ALL ${LUA_VANILLA_DLL_DEFINE} PRIVATE LUA_COMPAT_ALL ${LUA_VANILLA_DLL_DEFINE}) @@ -394,7 +402,7 @@ set_target_properties(${luacompiler} C_EXTENSIONS TRUE OUTPUT_NAME luac-${LUA_VANILLA_VERSION}) target_include_directories(${luacompiler} - PRIVATE ${LUA_VANILLA_SOURCE_DIR}) + PRIVATE "${LUA_VANILLA_INCLUDE_DIRS}") target_compile_options(${luacompiler} PRIVATE ${LUA_VANILLA_LUA_LUAC_COMPILER_OPTIONS}) target_compile_definitions(${luacompiler} diff --git a/include/sol/call.hpp b/include/sol/call.hpp index e5f98c38..27bc8d9a 100644 --- a/include/sol/call.hpp +++ b/include/sol/call.hpp @@ -167,7 +167,7 @@ namespace sol { typedef meta::tuple_types return_types; typedef typename traits::free_args_list args_list; // compile-time eliminate any functions that we know ahead of time are of improper arity - if (!traits::runtime_variadics_t::value && meta::find_in_pack_v, meta::index_value...>::value) { + if constexpr (!traits::runtime_variadics_t::value && meta::find_in_pack_v, meta::index_value...>::value) { return overload_match_arity(types<>(), std::index_sequence<>(), std::index_sequence(), std::forward(matchfx), L, fxarity, start, std::forward(args)...); } if (!traits::runtime_variadics_t::value && traits::free_arity != fxarity) { diff --git a/include/sol/compatibility/version.hpp b/include/sol/compatibility/version.hpp index e367f95b..a1f20235 100644 --- a/include/sol/compatibility/version.hpp +++ b/include/sol/compatibility/version.hpp @@ -27,44 +27,61 @@ #include "../feature_test.hpp" #if defined(SOL_USING_CXX_LUA) && SOL_USING_CXX_LUA -#include -#include -#include -#if defined(SOL_USING_CXX_LUAJIT) && SOL_USING_CXX_LUAJIT -#include -#endif // C++ LuaJIT ... whatever that means -#if (!defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) || !(SOL_EXCEPTIONS_SAFE_PROPAGATION)) && (!defined(SOL_EXCEPTIONS_ALWAYS_UNSAFE) || !(SOL_EXCEPTIONS_ALWAYS_UNSAFE)) -#define SOL_EXCEPTIONS_SAFE_PROPAGATION 1 -#endif // Exceptions can be propagated safely using C++-compiled Lua + #include + #include + #include + #if defined(SOL_USING_CXX_LUAJIT) && SOL_USING_CXX_LUAJIT + #include + #endif // C++ LuaJIT ... whatever that means + #if (!defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) || !(SOL_EXCEPTIONS_SAFE_PROPAGATION)) && (!defined(SOL_EXCEPTIONS_ALWAYS_UNSAFE) || !(SOL_EXCEPTIONS_ALWAYS_UNSAFE)) + #define SOL_EXCEPTIONS_SAFE_PROPAGATION 1 + #endif // Exceptions can be propagated safely using C++-compiled Lua #else -#include -#endif // C++ Mangling for Lua + #if defined(__has_include) + #if __has_include() + #include + #else + extern "C" { + #include + #include + #include + #if defined(SOL_LUAJIT) && SOL_LUAJIT + #include + #endif + } + #endif // lua.hpp exists or does not + #else + #include + #endif // check for lua.hpp safely for Lua 5.1 derps +#endif // C++ Mangling for Lua vs. Not #ifdef LUAJIT_VERSION -#ifndef SOL_LUAJIT -#define SOL_LUAJIT 1 -#ifndef SOL_LUAJIT_VERSION -#define SOL_LUAJIT_VERSION LUAJIT_VERSION_NUM -#endif // SOL_LUAJIT_VERSION definition, if not present -#endif // sol luajit + #ifndef SOL_LUAJIT + #define SOL_LUAJIT 1 + #endif // sol luajit + #if defined(SOL_LUAJIT) && SOL_LUAJIT + #ifndef SOL_LUAJIT_VERSION + #define SOL_LUAJIT_VERSION LUAJIT_VERSION_NUM + #endif // SOL_LUAJIT_VERSION definition, if not present + #endif #endif // luajit #if SOL_LUAJIT && SOL_LUAJIT_VERSION >= 20100 -#if !defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) && (!defined(SOL_EXCEPTIONS_ALWAYS_UNSAFE) && !(SOL_EXCEPTIONS_ALWAYS_UNSAFE)) -#define SOL_EXCEPTIONS_SAFE_PROPAGATION 1 -#endif // Do not catch (...) clauses + #if !defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) && (!defined(SOL_EXCEPTIONS_ALWAYS_UNSAFE) && !(SOL_EXCEPTIONS_ALWAYS_UNSAFE)) + #define SOL_EXCEPTIONS_SAFE_PROPAGATION 1 + #endif // Do not catch (...) clauses #endif // LuaJIT beta 02.01.00 have better exception handling on all platforms since beta3 #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502 -#define SOL_LUA_VERSION LUA_VERSION_NUM + #define SOL_LUA_VERSION LUA_VERSION_NUM #elif defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 -#define SOL_LUA_VERSION LUA_VERSION_NUM + #define SOL_LUA_VERSION LUA_VERSION_NUM #elif !defined(LUA_VERSION_NUM) || !(LUA_VERSION_NUM) -// Definitely 5.0 -#define SOL_LUA_VERSION 500 + // Definitely 5.0 + #define SOL_LUA_VERSION 500 #else -// ??? Not sure, assume 502? -#define SOL_LUA_VERSION 502 -#endif // Lua Version 502, 501 || luajit, 500 + // ??? Not sure, assume 503? + #define SOL_LUA_VERSION 503 +#endif // Lua Version 503, 502, 501 || luajit, 500 #endif // SOL_VERSION_HPP diff --git a/include/sol/demangle.hpp b/include/sol/demangle.hpp index a4836613..b9b525af 100644 --- a/include/sol/demangle.hpp +++ b/include/sol/demangle.hpp @@ -24,6 +24,7 @@ #ifndef SOL_DEMANGLE_HPP #define SOL_DEMANGLE_HPP +#include "string_view.hpp" #include #include #include @@ -36,12 +37,22 @@ extern "C" { namespace sol { namespace detail { + inline constexpr std::array removals{ { "{anonymous}", + "(anonymous namespace)", + "public:", + "private:", + "protected:", + "struct ", + "class ", + "`anonymous-namespace'", + "`anonymous namespace'" } }; + + #if defined(__GNUC__) || defined(__clang__) template inline std::string ctti_get_type_name() { // cardinal sins from MINGW using namespace std; - static const std::array removals = {{"{anonymous}", "(anonymous namespace)"}}; std::string name = __PRETTY_FUNCTION__; std::size_t start = name.find_first_of('['); start = name.find_first_of('=', start); @@ -75,7 +86,6 @@ namespace detail { #elif defined(_MSC_VER) template inline std::string ctti_get_type_name() { - static const std::array removals = {{"public:", "private:", "protected:", "struct ", "class ", "`anonymous-namespace'", "`anonymous namespace'"}}; std::string name = __FUNCSIG__; std::size_t start = name.find("get_type_name"); if (start == std::string::npos) diff --git a/include/sol/error_handler.hpp b/include/sol/error_handler.hpp index a2347bf0..3a2d72f7 100644 --- a/include/sol/error_handler.hpp +++ b/include/sol/error_handler.hpp @@ -58,7 +58,7 @@ namespace sol { const char* name = lua_tolstring(L, -1, &sz); std::string tn(name, static_cast(sz)); lua_pop(L, 2); - return name; + return std::move(tn); } default: break; diff --git a/include/sol/forward.hpp b/include/sol/forward.hpp index edc987fd..ca023e24 100644 --- a/include/sol/forward.hpp +++ b/include/sol/forward.hpp @@ -202,6 +202,14 @@ namespace sol { struct record; } +#if !defined(SOL_USE_BOOST) || (SOL_USE_BOOST == 0) + template + class optional; + + template + class optional; +#endif + } // namespace sol #define SOL_BASE_CLASSES(T, ...) \ diff --git a/include/sol/metatable.hpp b/include/sol/metatable.hpp index 8e2e598e..9d985c08 100644 --- a/include/sol/metatable.hpp +++ b/include/sol/metatable.hpp @@ -36,22 +36,19 @@ namespace sol { friend class state_view; protected: - basic_metatable(detail::no_safety_tag, lua_nil_t n) - : base_t(n) { + basic_metatable(detail::no_safety_tag, lua_nil_t n) : base_t(n) { } - basic_metatable(detail::no_safety_tag, lua_State* L, int index) - : base_t(L, index) { + basic_metatable(detail::no_safety_tag, lua_State* L, int index) : base_t(L, index) { } - basic_metatable(detail::no_safety_tag, lua_State* L, ref_index index) - : base_t(L, index) { + basic_metatable(detail::no_safety_tag, lua_State* L, ref_index index) : base_t(L, index) { } - template , basic_metatable>>, meta::neg>, meta::neg>>, is_lua_reference>> = meta::enabler> - basic_metatable(detail::no_safety_tag, T&& r) noexcept - : base_t(std::forward(r)) { + template , basic_metatable>>, meta::neg>, + meta::neg>>, is_lua_reference>> = meta::enabler> + basic_metatable(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward(r)) { } template >> = meta::enabler> - basic_metatable(detail::no_safety_tag, lua_State* L, T&& r) noexcept - : base_t(L, std::forward(r)) { + basic_metatable(detail::no_safety_tag, lua_State* L, T&& r) noexcept : base_t(L, std::forward(r)) { } public: @@ -62,39 +59,35 @@ namespace sol { basic_metatable(basic_metatable&&) = default; basic_metatable& operator=(const basic_metatable&) = default; basic_metatable& operator=(basic_metatable&&) = default; - basic_metatable(const stack_reference& r) - : basic_metatable(r.lua_state(), r.stack_index()) { + basic_metatable(const stack_reference& r) : basic_metatable(r.lua_state(), r.stack_index()) { } - basic_metatable(stack_reference&& r) - : basic_metatable(r.lua_state(), r.stack_index()) { + basic_metatable(stack_reference&& r) : basic_metatable(r.lua_state(), r.stack_index()) { } template >> = meta::enabler> - basic_metatable(lua_State* L, T&& r) - : base_t(L, std::forward(r)) { + basic_metatable(lua_State* L, T&& r) : base_t(L, std::forward(r)) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(lua_state(), -1, handler); #endif // Safety } - basic_metatable(lua_State* L, int index = -1) - : basic_metatable(detail::no_safety, L, index) { + basic_metatable(lua_State* L, int index = -1) : basic_metatable(detail::no_safety, L, index) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES constructor_handler handler{}; stack::check(L, index, handler); #endif // Safety } - basic_metatable(lua_State* L, ref_index index) - : basic_metatable(detail::no_safety, L, index) { + basic_metatable(lua_State* L, ref_index index) : basic_metatable(detail::no_safety, L, index) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(lua_state(), -1, handler); #endif // Safety } - template , basic_metatable>>, meta::neg>, meta::neg>>, is_lua_reference>> = meta::enabler> - basic_metatable(T&& r) noexcept - : basic_metatable(detail::no_safety, std::forward(r)) { + template , basic_metatable>>, meta::neg>, + meta::neg>>, is_lua_reference>> = meta::enabler> + basic_metatable(T&& r) noexcept : basic_metatable(detail::no_safety, std::forward(r)) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES if (!is_table>::value) { auto pp = stack::push_pop(*this); @@ -103,8 +96,7 @@ namespace sol { } #endif // Safety } - basic_metatable(lua_nil_t r) noexcept - : basic_metatable(detail::no_safety, r) { + basic_metatable(lua_nil_t r) noexcept : basic_metatable(detail::no_safety, r) { } template @@ -128,11 +120,11 @@ namespace sol { } ustorage_base& base_storage = *static_cast(stack::get(L, -1)); base_storage.clear(); - stack_reference gnt(L, -1); + stack_reference gc_names_table(L, -1); std::array registry_traits; for (int i = 0; i < registry_traits.size(); ++i) { u_detail::submetatable_type smt = static_cast(i); - stack::get_field(L, smt, gnt.stack_index()); + stack::get_field(L, smt, gc_names_table.stack_index()); registry_traits[i] = stack::get(L, -1); } // get the registry diff --git a/include/sol/optional.hpp b/include/sol/optional.hpp index 66e93597..1325a62e 100644 --- a/include/sol/optional.hpp +++ b/include/sol/optional.hpp @@ -24,7 +24,7 @@ #ifndef SOL_OPTIONAL_HPP #define SOL_OPTIONAL_HPP -#include "compatibility.hpp" +#include "forward.hpp" #include "in_place.hpp" #if defined(SOL_USE_BOOST) && SOL_USE_BOOST #include @@ -32,6 +32,10 @@ #include "optional_implementation.hpp" #endif // Boost vs. Better optional +#if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES +#include +#endif + namespace sol { #if defined(SOL_USE_BOOST) && SOL_USE_BOOST @@ -46,6 +50,10 @@ namespace sol { struct is_optional : std::false_type {}; template struct is_optional> : std::true_type {}; +#if defined(SOL_CXX17_FEATURES) && SOL_CXX17_FEATURES + template + struct is_optional> : std::true_type {}; +#endif } // namespace meta } // namespace sol diff --git a/include/sol/optional_implementation.hpp b/include/sol/optional_implementation.hpp index 50f02075..06b319bc 100644 --- a/include/sol/optional_implementation.hpp +++ b/include/sol/optional_implementation.hpp @@ -1,5 +1,3 @@ -// sol3 - // The MIT License (MIT) // Copyright (c) 2013-2018 Rapptz, ThePhD and contributors @@ -21,1131 +19,2255 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// Copyright (C) 2011 - 2012 Andrzej Krzemienski. -// Use, modification, and distribution is subject to the Boost Software -// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -// The idea and interface is based on Boost.Optional library -// authored by Fernando Luis Cacciola Carballal +// Taken from: TartanLlama/optional on Github, because +// holy shit am I done dealing with C++11 constexpr -#ifndef SOL_OPTIONAL_IMPLEMENTATION_HPP -#define SOL_OPTIONAL_IMPLEMENTATION_HPP +/// +// optional - An implementation of std::optional with extensions +// Written in 2017 by Simon Brand (@TartanLlama) +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef SOL_TL_OPTIONAL_HPP +#define SOL_TL_OPTIONAL_HPP #include "in_place.hpp" -#include -#include -#include -#include +#define SOL_TL_OPTIONAL_VERSION_MAJOR 0 +#define SOL_TL_OPTIONAL_VERSION_MINOR 5 + +#include #include -#include -#include -#if defined(SOL_NO_EXCEPTIONS) && SOL_NO_EXCEPTIONS -#include -#endif // Exceptions +#include +#include +#include -#define TR2_OPTIONAL_REQUIRES(...) typename ::std::enable_if<__VA_ARGS__::value, bool>::type = false - -#if defined __GNUC__ // NOTE: GNUC is also defined for Clang -#if (__GNUC__ >= 5) -#define TR2_OPTIONAL_GCC_5_0_AND_HIGHER___ -#define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ -#elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) -#define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ -#elif (__GNUC__ > 4) -#define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ -#endif -# -#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7) -#define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ -#elif (__GNUC__ > 4) -#define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ -#endif -# -#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1) -#define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -#elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) -#define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -#elif (__GNUC__ > 4) -#define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -#endif -#endif -# -#if defined __clang_major__ -#if (__clang_major__ == 3 && __clang_minor__ >= 5) -#define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ -#elif (__clang_major__ > 3) -#define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ -#endif -#if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ -#define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ -#elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2) -#define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ -#endif -#endif -# -#if defined _MSC_VER -#if (_MSC_VER >= 1900) -#define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ -#endif +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define SOL_TL_OPTIONAL_MSVC2015 #endif -#if defined __clang__ -#if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) -#define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__)) +#define SOL_TL_OPTIONAL_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && !defined(__clang__)) +#define SOL_TL_OPTIONAL_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && !defined(__clang__)) +#define SOL_TL_OPTIONAL_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions +#define SOL_TL_OPTIONAL_NO_CONSTRR + +// GCC < 5 doesn't support some standard C++11 type traits +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::has_trivial_copy_constructor::value +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value + +// This one will be different for GCC 5.7 if it's ever supported +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector +// for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef SOL_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define SOL_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace sol { namespace detail { + template + struct is_trivially_copy_constructible : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR + template + struct is_trivially_copy_constructible> : std::is_trivially_copy_constructible {}; +#endif +}} // namespace sol::detail +#endif + +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) tl::detail::is_trivially_copy_constructible::value +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable::value +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value #else -#define OPTIONAL_HAS_THIS_RVALUE_REFS 0 -#endif -#elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -#define OPTIONAL_HAS_THIS_RVALUE_REFS 1 -#elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ -#define OPTIONAL_HAS_THIS_RVALUE_REFS 1 -#else -#define OPTIONAL_HAS_THIS_RVALUE_REFS 0 +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::is_trivially_copy_constructible::value +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable::value +#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value #endif -#if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -#define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 -#define OPTIONAL_CONSTEXPR_INIT_LIST constexpr -#else -#define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 -#define OPTIONAL_CONSTEXPR_INIT_LIST +#if __cplusplus > 201103L +#define SOL_TL_OPTIONAL_CXX14 #endif -#if defined(TR2_OPTIONAL_MSVC_2015_AND_HIGHER___) || (defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L)) -#define OPTIONAL_HAS_MOVE_ACCESSORS 1 +// constexpr implies const in C++11, not C++14 +#if (__cplusplus == 201103L || defined(SOL_TL_OPTIONAL_MSVC2015) || defined(SOL_TL_OPTIONAL_GCC49)) +/// \exclude +#define SOL_TL_OPTIONAL_11_CONSTEXPR #else -#define OPTIONAL_HAS_MOVE_ACCESSORS 0 -#endif - -#// In C++11 constexpr implies const, so we need to make non-const members also non-constexpr -#if defined(TR2_OPTIONAL_MSVC_2015_AND_HIGHER___) || ((defined __cplusplus) && (__cplusplus == 201103L)) -#define OPTIONAL_MUTABLE_CONSTEXPR -#else -#define OPTIONAL_MUTABLE_CONSTEXPR constexpr -#endif - -#if defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ -#pragma warning(push) -#pragma warning(disable : 4814) +/// \exclude +#define SOL_TL_OPTIONAL_11_CONSTEXPR constexpr #endif namespace sol { - - // BEGIN workaround for missing is_trivially_destructible -#if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ - // leave it: it is already there -#elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ - // leave it: it is already there -#elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ - // leave it: it is already there -#elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS - // leave it: the user doesn't want it -#else - template - using is_trivially_destructible = ::std::has_trivial_destructor; -#endif - // END workaround for missing is_trivially_destructible - -#if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___) - // leave it; our metafunctions are already defined. -#elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ - // leave it; our metafunctions are already defined. -#elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ - // leave it: it is already there -#elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS - // leave it: the user doesn't want it -#else - - // workaround for missing traits in GCC and CLANG - template - struct is_nothrow_move_constructible { - static constexprbool value = ::std::is_nothrow_constructible::value; - }; - - template - struct is_assignable { - template - static constexprbool has_assign(...) { - return false; - } - - template () = ::std::declval(), true))> - // the comma operator is necessary for the cases where operator= returns void - static constexprbool has_assign(bool) { - return true; - } - - static constexprbool value = has_assign(true); - }; - - template - struct is_nothrow_move_assignable { - template - struct has_nothrow_move_assign { - static constexprbool value = false; - }; - - template - struct has_nothrow_move_assign { - static constexprbool value = noexcept(::std::declval() = ::std::declval()); - }; - - static constexprbool value = has_nothrow_move_assign::value>::value; - }; - // end workaround - +#ifndef SOL_TL_MONOSTATE_INPLACE_MUTEX +#define SOL_TL_MONOSTATE_INPLACE_MUTEX + /// \brief Used to represent an optional with no data; essentially a bool + class monostate {}; #endif - // 20.5.4, optional for object types template class optional; - // 20.5.5, optional for lvalue reference types - template - class optional; + /// \exclude + namespace detail { +#ifndef SOL_TL_TRAITS_MUTEX +#define SOL_TL_TRAITS_MUTEX + // C++14-style aliases for brevity + template + using remove_const_t = typename std::remove_const::type; + template + using remove_reference_t = typename std::remove_reference::type; + template + using decay_t = typename std::decay::type; + template + using enable_if_t = typename std::enable_if::type; + template + using conditional_t = typename std::conditional::type; - // workaround: std utility functions aren't constexpr yet - template - inline constexpr T&& constexpr_forward(typename ::std::remove_reference::type& t) noexcept { - return static_cast(t); - } + // std::conjunction from C++17 + template + struct conjunction : std::true_type {}; + template + struct conjunction : B {}; + template + struct conjunction : std::conditional, B>::type {}; - template - inline constexpr T&& constexpr_forward(typename ::std::remove_reference::type&& t) noexcept { - static_assert(!::std::is_lvalue_reference::value, "!!"); - return static_cast(t); - } - - template - inline constexpr typename ::std::remove_reference::type&& constexpr_move(T&& t) noexcept { - return static_cast::type&&>(t); - } - -#if defined NDEBUG -#define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) -#else -#define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([] { assert(!#CHECK); }(), (EXPR))) +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND #endif - namespace detail_ { +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND + template + struct is_pointer_to_non_const_member_func : std::false_type {}; + template + struct is_pointer_to_non_const_member_func : std::true_type {}; + template + struct is_pointer_to_non_const_member_func : std::true_type {}; + template + struct is_pointer_to_non_const_member_func : std::true_type {}; + template + struct is_pointer_to_non_const_member_func : std::true_type {}; + template + struct is_pointer_to_non_const_member_func : std::true_type {}; + template + struct is_pointer_to_non_const_member_func : std::true_type {}; - // static_addressof: a constexpr version of addressof - template - struct has_overloaded_addressof { - template - static constexpr bool has_overload(...) { - return false; - } + template + struct is_const_or_const_ref : std::false_type {}; + template + struct is_const_or_const_ref : std::true_type {}; + template + struct is_const_or_const_ref : std::true_type {}; +#endif - template ().operator&())> - static constexpr bool has_overload(bool) { - return true; - } + // std::invoke from C++17 + // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround + template ::value && is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> + constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); + } - static constexpr bool value = has_overload(true); + template >::value>> + constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); + } + + // std::invoke_result from C++17 + template + struct invoke_result_impl; + + template + struct invoke_result_impl(), std::declval()...), void()), Us...> { + using type = decltype(detail::invoke(std::declval(), std::declval()...)); }; - template )> - constexpr T* static_addressof(T& ref) { - return &ref; - } + template + using invoke_result = invoke_result_impl; - template )> - T* static_addressof(T& ref) { - return ::std::addressof(ref); - } + template + using invoke_result_t = typename invoke_result::type; +#endif - // the call to convert(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A + // std::void_t from C++17 + template + struct voider { + using type = void; + }; + template + using void_t = typename voider::type; + + // Trait for checking if a type is a tl::optional + template + struct is_optional_impl : std::false_type {}; + template + struct is_optional_impl> : std::true_type {}; + template + using is_optional = is_optional_impl>; + + // Change void to tl::monostate template - constexpr U convert(U v) { - return v; - } + using fixup_void = conditional_t::value, monostate, U>; - } // namespace detail_ + template > + using get_map_return = optional>>; - constexpr struct trivial_init_t { - } trivial_init{}; + // Check if invoking F for some Us returns void + template + struct returns_void_impl; + template + struct returns_void_impl>, U...> : std::is_void> {}; + template + using returns_void = returns_void_impl; - // 20.5.7, Disengaged state indicator + template + using enable_if_ret_void = enable_if_t::value>; + + template + using disable_if_ret_void = enable_if_t::value>; + + template + using enable_forward_value = detail::enable_if_t::value && !std::is_same, in_place_t>::value + && !std::is_same, detail::decay_t>::value>; + + template + using enable_from_other = detail::enable_if_t::value && !std::is_constructible&>::value + && !std::is_constructible&&>::value && !std::is_constructible&>::value + && !std::is_constructible&&>::value && !std::is_convertible&, T>::value + && !std::is_convertible&&, T>::value && !std::is_convertible&, T>::value + && !std::is_convertible&&, T>::value>; + + template + using enable_assign_forward = detail::enable_if_t, detail::decay_t>::value + && !detail::conjunction, std::is_same>>::value && std::is_constructible::value + && std::is_assignable::value>; + + template + using enable_assign_from_other = detail::enable_if_t::value && std::is_assignable::value + && !std::is_constructible&>::value && !std::is_constructible&&>::value + && !std::is_constructible&>::value && !std::is_constructible&&>::value + && !std::is_convertible&, T>::value && !std::is_convertible&&, T>::value + && !std::is_convertible&, T>::value && !std::is_convertible&&, T>::value + && !std::is_assignable&>::value && !std::is_assignable&&>::value + && !std::is_assignable&>::value && !std::is_assignable&&>::value>; + +#ifdef _MSC_VER + // TODO make a version which works with MSVC + template + struct is_swappable : std::true_type {}; + + template + struct is_nothrow_swappable : std::true_type {}; +#else + // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept + namespace swap_adl_tests { + // if swap ADL finds this then it would call std::swap otherwise (same + // signature) + struct tag {}; + + template + tag swap(T&, T&); + template + tag swap(T (&a)[N], T (&b)[N]); + + // helper functions to test if an unqualified swap is possible, and if it + // becomes std::swap + template + std::false_type can_swap(...) noexcept(false); + template (), std::declval()))> + std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), std::declval()))); + + template + std::false_type uses_std(...); + template + std::is_same(), std::declval())), tag> uses_std(int); + + template + struct is_std_swap_noexcept + : std::integral_constant::value && std::is_nothrow_move_assignable::value> {}; + + template + struct is_std_swap_noexcept : is_std_swap_noexcept {}; + + template + struct is_adl_swap_noexcept : std::integral_constant(0))> {}; + } // namespace swap_adl_tests + + template + struct is_swappable : std::integral_constant(0))::value + && (!decltype(detail::swap_adl_tests::uses_std(0))::value + || (std::is_move_assignable::value && std::is_move_constructible::value))> {}; + + template + struct is_swappable : std::integral_constant(0))::value + && (!decltype(detail::swap_adl_tests::uses_std(0))::value || is_swappable::value)> {}; + + template + struct is_nothrow_swappable + : std::integral_constant::value + && ((decltype(detail::swap_adl_tests::uses_std(0))::value&& detail::swap_adl_tests::is_std_swap_noexcept::value) + || (!decltype(detail::swap_adl_tests::uses_std(0))::value&& detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif + + // The storage base manages the actual storage, and correctly propagates + // trivial destruction from T. This case is for when T is not trivially + // destructible. + template ::value> + struct optional_storage_base { + SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) { + } + + template + SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) : m_value(std::forward(u)...), m_has_value(true) { + } + + ~optional_storage_base() { + if (m_has_value) { + m_value.~T(); + m_has_value = false; + } + } + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value; + }; + + // This case is for when T is trivially destructible. + template + struct optional_storage_base { + SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) { + } + + template + SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) : m_value(std::forward(u)...), m_has_value(true) { + } + + // No destructor, so this class is trivially destructible + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value = false; + }; + + // This base class provides some handy member functions which can be used in + // further derived classes + template + struct optional_operations_base : optional_storage_base { + using optional_storage_base::optional_storage_base; + + void hard_reset() noexcept { + get().~T(); + this->m_has_value = false; + } + + template + void construct(Args&&... args) noexcept { + new (std::addressof(this->m_value)) T(std::forward(args)...); + this->m_has_value = true; + } + + template + void assign(Opt&& rhs) { + if (this->has_value()) { + if (rhs.has_value()) { + this->m_value = std::forward(rhs).get(); + } + else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + else if (rhs.has_value()) { + construct(std::forward(rhs).get()); + } + } + + bool has_value() const { + return this->m_has_value; + } + + SOL_TL_OPTIONAL_11_CONSTEXPR T& get() & { + return this->m_value; + } + SOL_TL_OPTIONAL_11_CONSTEXPR const T& get() const& { + return this->m_value; + } + SOL_TL_OPTIONAL_11_CONSTEXPR T&& get() && { + return std::move(this->m_value); + } +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + constexpr const T&& get() const&& { + return std::move(this->m_value); + } +#endif + }; + + // This class manages conditionally having a trivial copy constructor + // This specialization is for when T is trivially copy constructible + template + struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; + }; + + // This specialization is for when T is not trivially copy constructible + template + struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; + + optional_copy_base() = default; + optional_copy_base(const optional_copy_base& rhs) { + if (rhs.has_value()) { + this->construct(rhs.get()); + } + else { + this->m_has_value = false; + } + } + + optional_copy_base(optional_copy_base&& rhs) = default; + optional_copy_base& operator=(const optional_copy_base& rhs) = default; + optional_copy_base& operator=(optional_copy_base&& rhs) = default; + }; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef SOL_TL_OPTIONAL_GCC49 + template ::value> + struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; + }; +#else + template + struct optional_move_base; +#endif + template + struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; + + optional_move_base() = default; + optional_move_base(const optional_move_base& rhs) = default; + + optional_move_base(optional_move_base&& rhs) noexcept(std::is_nothrow_move_constructible::value) { + if (rhs.has_value()) { + this->construct(std::move(rhs.get())); + } + else { + this->m_has_value = false; + } + } + optional_move_base& operator=(const optional_move_base& rhs) = default; + optional_move_base& operator=(optional_move_base&& rhs) = default; + }; + + // This class manages conditionally having a trivial copy assignment operator + template + struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; + }; + + template + struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; + + optional_copy_assign_base() = default; + optional_copy_assign_base(const optional_copy_assign_base& rhs) = default; + + optional_copy_assign_base(optional_copy_assign_base&& rhs) = default; + optional_copy_assign_base& operator=(const optional_copy_assign_base& rhs) { + this->assign(rhs); + return *this; + } + optional_copy_assign_base& operator=(optional_copy_assign_base&& rhs) = default; + }; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef SOL_TL_OPTIONAL_GCC49 + template ::value&& std::is_trivially_move_constructible::value&& std::is_trivially_move_assignable::value> + struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; + }; +#else + template + struct optional_move_assign_base; +#endif + + template + struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; + + optional_move_assign_base() = default; + optional_move_assign_base(const optional_move_assign_base& rhs) = default; + + optional_move_assign_base(optional_move_assign_base&& rhs) = default; + + optional_move_assign_base& operator=(const optional_move_assign_base& rhs) = default; + + optional_move_assign_base& operator=(optional_move_assign_base&& rhs) noexcept( + std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } + }; + + // optional_delete_ctor_base will conditionally delete copy and move + // constructors depending on whether T is copy/move constructible + template ::value, bool EnableMove = std::is_move_constructible::value> + struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default; + optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default; + }; + + template + struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete; + optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default; + }; + + template + struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = delete; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default; + optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default; + }; + + template + struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = delete; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete; + optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default; + }; + + // optional_delete_assign_base will conditionally delete copy and move + // constructors depending on whether T is copy/move constructible + assignable + template ::value && std::is_copy_assignable::value), + bool EnableMove = (std::is_move_constructible::value && std::is_move_assignable::value)> + struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& operator=(const optional_delete_assign_base&) = default; + optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = default; + }; + + template + struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& operator=(const optional_delete_assign_base&) = default; + optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = delete; + }; + + template + struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& operator=(const optional_delete_assign_base&) = delete; + optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = default; + }; + + template + struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& operator=(const optional_delete_assign_base&) = delete; + optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = delete; + }; + + } // namespace detail + + /// \brief A tag type to represent an empty optional struct nullopt_t { - struct init {}; - constexpr explicit nullopt_t(init) { + struct do_not_use {}; + constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept { } }; - constexpr nullopt_t nullopt{nullopt_t::init()}; + /// \brief Represents an empty optional + /// \synopsis static constexpr nullopt_t nullopt; + /// + /// *Examples*: + /// ``` + /// tl::optional a = tl::nullopt; + /// void foo (tl::optional); + /// foo(tl::nullopt); //pass an empty optional + /// ``` + static constexpr nullopt_t nullopt{ nullopt_t::do_not_use{}, nullopt_t::do_not_use{} }; - // 20.5.8, class bad_optional_access - class bad_optional_access : public ::std::logic_error { + class bad_optional_access : public std::exception { public: - explicit bad_optional_access(const ::std::string& what_arg) - : ::std::logic_error{what_arg} { - } - explicit bad_optional_access(const char* what_arg) - : ::std::logic_error{what_arg} { + bad_optional_access() = default; + const char* what() const noexcept { + return "Optional has no value"; } }; + /// An optional object is an object that contains the storage for another + /// object and manages the lifetime of this contained object, if any. The + /// contained object may be initialized after the optional object has been + /// initialized, and may be destroyed before the optional object has been + /// destroyed. The initialization state of the contained object is tracked by + /// the optional object. template - struct alignas(T) optional_base { - char storage_[sizeof(T)]; - bool init_; + class optional : private detail::optional_move_assign_base, + private detail::optional_delete_ctor_base, + private detail::optional_delete_assign_base { + using base = detail::optional_move_assign_base; - constexpr optional_base() noexcept - : storage_(), init_(false){}; + static_assert(!std::is_same::value, "instantiation of optional with in_place_t is ill-formed"); + static_assert(!std::is_same, nullopt_t>::value, "instantiation of optional with nullopt_t is ill-formed"); - explicit optional_base(const T& v) - : storage_(), init_(true) { - new (&storage()) T(v); + public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55) + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - explicit optional_base(T&& v) - : storage_(), init_(true) { - new (&storage()) T(constexpr_move(v)); + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } - template - explicit optional_base(in_place_t, Args&&... args) - : init_(true), storage_() { - new (&storage()) T(constexpr_forward(args)...); + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr auto and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - template >)> - explicit optional_base(in_place_t, ::std::initializer_list il, Args&&... args) - : init_(true), storage_() { - new (&storage()) T(il, constexpr_forward(args)...); - } -#if defined __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - T& storage() { - return *reinterpret_cast(&storage_[0]); - } +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr auto and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); - constexpr const T& storage() const { - return *reinterpret_cast(&storage_[0]); - } -#if defined __GNUC__ -#pragma GCC diagnostic pop -#endif - - ~optional_base() { - if (init_) { - storage().T::~T(); - } - } - }; - -#if defined __GNUC__ && !defined TR2_OPTIONAL_GCC_5_0_AND_HIGHER___ - // Sorry, GCC 4.x; you're just a piece of shit - template - using constexpr_optional_base = optional_base; -#else - template - struct alignas(T) constexpr_optional_base { - char storage_[sizeof(T)]; - bool init_; - constexpr constexpr_optional_base() noexcept - : storage_(), init_(false) { - } - - explicit constexpr constexpr_optional_base(const T& v) - : storage_(), init_(true) { - new (&storage()) T(v); - } - - explicit constexpr constexpr_optional_base(T&& v) - : storage_(), init_(true) { - new (&storage()) T(constexpr_move(v)); - } - - template - explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) - : init_(true), storage_() { - new (&storage()) T(constexpr_forward(args)...); - } - - template >)> - OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, ::std::initializer_list il, Args&&... args) - : init_(true), storage_() { - new (&storage()) T(il, constexpr_forward(args)...); - } - -#if defined __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - T& storage() { - return (*reinterpret_cast(&storage_[0])); - } - - constexpr const T& storage() const { - return (*reinterpret_cast(&storage_[0])); - } -#if defined __GNUC__ -#pragma GCC diagnostic pop -#endif - - ~constexpr_optional_base() = default; - }; -#endif - - template - using OptionalBase = typename ::std::conditional< - ::std::is_trivially_destructible::value, - constexpr_optional_base::type>, - optional_base::type>>::type; - - template - class optional : private OptionalBase { - static_assert(!::std::is_same::type, nullopt_t>::value, "bad T"); - static_assert(!::std::is_same::type, in_place_t>::value, "bad T"); - - constexpr bool initialized() const noexcept { - return OptionalBase::init_; - } - typename ::std::remove_const::type* dataptr() { - return ::std::addressof(OptionalBase::storage()); - } - constexpr const T* dataptr() const { - return detail_::static_addressof(OptionalBase::storage()); - } - -#if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 - constexpr const T& contained_val() const& { - return OptionalBase::storage(); - } -#if OPTIONAL_HAS_MOVE_ACCESSORS == 1 - OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { - return ::std::move(OptionalBase::storage()); - } - OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { - return OptionalBase::storage(); - } -#else - T& contained_val() & { - return OptionalBase::storage(); - } - T&& contained_val() && { - return ::std::move(OptionalBase::storage()); + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); } #endif #else - constexpr const T& contained_val() const { - return OptionalBase::storage(); + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise the return value of + /// `std::invoke(std::forward(f), value())` is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - T& contained_val() { - return OptionalBase::storage(); + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); + } + + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr detail::invoke_result_t and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr detail::invoke_result_t and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : result(nullopt); + } +#endif +#endif + +#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & { + return optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&; + template + constexpr auto map(F&& f) const& { + return optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&&; + template + constexpr auto map(F&& f) const&& { + return optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template auto map(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), std::declval())) map(F&& f) & { + return optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), std::declval())) map(F&& f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) const&; + template + constexpr decltype(optional_map_impl(std::declval(), std::declval())) map(F&& f) const& { + return optional_map_impl(*this, std::forward(f)); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group map + /// \synopsis template auto map(F &&f) const&&; + template + constexpr decltype(optional_map_impl(std::declval(), std::declval())) map(F&& f) const&& { + return optional_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + + /// \brief Calls `f` if the optional is empty + /// \requires `std::invoke_result_t` must be void or convertible to + /// `optional`. + /// \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// + /// \group or_else + /// \synopsis template optional or_else (F &&f) &; + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + return has_value() ? *this : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) &&; + template * = nullptr> + optional or_else(F&& f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; + template * = nullptr> + optional or_else(F&& f) const& { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& { + return has_value() ? *this : std::forward(f)(); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \exclude + template * = nullptr> + optional or_else(F&& f) const&& { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional or_else(F&& f) const&& { + return has_value() ? std::move(*this) : std::forward(f)(); } #endif - void clear() noexcept { - if (initialized()) - dataptr()->T::~T(); - OptionalBase::init_ = false; + /// \brief Maps the stored value with `f` if there is one, otherwise returns + /// `u`. + /// + /// \details If there is a value stored, then `f` is called with `**this` + /// and the value is returned. Otherwise `u` is returned. + /// + /// \group map_or + template + U map_or(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); } + /// \group map_or + template + U map_or(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); + } + + /// \group map_or + template + U map_or(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group map_or + template + U map_or(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + /// + /// \details If there is a value stored, then `f` is + /// called with `**this` and the value is returned. Otherwise + /// `std::forward(u)()` is returned. + /// + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// &&; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &&; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); + } +#endif + + /// \returns `u` if `*this` has a value, otherwise an empty optional. + template + constexpr optional::type> conjunction(U&& u) const { + using result = optional>; + return has_value() ? result{ u } : result{ nullopt }; + } + + /// \returns `rhs` if `*this` is empty, otherwise the current value. + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & { + return has_value() ? *this : rhs; + } + + /// \group disjunction + constexpr optional disjunction(const optional& rhs) const& { + return has_value() ? *this : rhs; + } + + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(const optional& rhs) const&& { + return has_value() ? std::move(*this) : rhs; + } +#endif + + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + constexpr optional disjunction(optional&& rhs) const& { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(optional&& rhs) const&& { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + /// \group take + optional take() & { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() const& { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() && { + optional ret = std::move(*this); + reset(); + return ret; + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group take + optional take() const&& { + optional ret = std::move(*this); + reset(); + return ret; + } +#endif + + using value_type = T; + + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept = default; + + /// \group ctor_empty + constexpr optional(nullopt_t) noexcept { + } + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + SOL_TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + SOL_TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default; + + /// Constructs the stored value in-place using the given arguments. + /// \group in_place + /// \synopsis template constexpr explicit optional(in_place_t, Args&&... args); template - void initialize(Args&&... args) noexcept(noexcept(T(::std::forward(args)...))) { - assert(!OptionalBase::init_); - ::new (static_cast(dataptr())) T(::std::forward(args)...); - OptionalBase::init_ = true; + constexpr explicit optional(detail::enable_if_t::value, in_place_t>, Args&&... args) + : base(in_place, std::forward(args)...) { } + /// \group in_place + /// \synopsis template \nconstexpr explicit optional(in_place_t, std::initializer_list&, Args&&... args); template - void initialize(::std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, ::std::forward(args)...))) { - assert(!OptionalBase::init_); - ::new (static_cast(dataptr())) T(il, ::std::forward(args)...); - OptionalBase::init_ = true; + SOL_TL_OPTIONAL_11_CONSTEXPR explicit optional(detail::enable_if_t&, Args&&...>::value, in_place_t>, + std::initializer_list il, Args&&... args) { + this->construct(il, std::forward(args)...); } - public: - typedef T value_type; - - // 20.5.5.1, constructors - constexpr optional() noexcept - : OptionalBase(){}; - constexpr optional(nullopt_t) noexcept - : OptionalBase(){}; - - optional(const optional& rhs) - : OptionalBase() { - if (rhs.initialized()) { - ::new (static_cast(dataptr())) T(*rhs); - OptionalBase::init_ = true; - } +#if 0 // SOL_MODIFICATION + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template ::value>* = nullptr, detail::enable_forward_value* = nullptr> + constexpr optional(U&& u) : base(in_place, std::forward(u)) { } - optional(const optional& rhs) - : optional() { - if (rhs) { - ::new (static_cast(dataptr())) T(*rhs); - OptionalBase::init_ = true; - } + /// \exclude + template ::value>* = nullptr, detail::enable_forward_value* = nullptr> + constexpr explicit optional(U&& u) : base(in_place, std::forward(u)) { + } +#else + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + constexpr optional(T&& u) : base(in_place, std::move(u)) { } - optional(optional&& rhs) noexcept(::std::is_nothrow_move_constructible::value) - : OptionalBase() { - if (rhs.initialized()) { - ::new (static_cast(dataptr())) T(::std::move(*rhs)); - OptionalBase::init_ = true; - } + /// \exclude + constexpr optional(const T& u) : base(in_place, u) { + } +#endif // sol2 modification + + /// Converting copy constructor. + /// \synopsis template optional(const optional &rhs); + template * = nullptr, detail::enable_if_t::value>* = nullptr> + optional(const optional& rhs) { + this->construct(*rhs); } - constexpr optional(const T& v) - : OptionalBase(v) { + /// \exclude + template * = nullptr, detail::enable_if_t::value>* = nullptr> + explicit optional(const optional& rhs) { + this->construct(*rhs); } - constexpr optional(T&& v) - : OptionalBase(constexpr_move(v)) { + /// Converting move constructor. + /// \synopsis template optional(optional &&rhs); + template * = nullptr, detail::enable_if_t::value>* = nullptr> + optional(optional&& rhs) { + this->construct(std::move(*rhs)); } - template - explicit constexpr optional(in_place_t, Args&&... args) - : OptionalBase(in_place, constexpr_forward(args)...) { + /// \exclude + template * = nullptr, detail::enable_if_t::value>* = nullptr> + explicit optional(optional&& rhs) { + this->construct(std::move(*rhs)); } - template >)> - OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, ::std::initializer_list il, Args&&... args) - : OptionalBase(in_place, il, constexpr_forward(args)...) { - } - - // 20.5.4.2, Destructor + /// Destroys the stored value if there is one. ~optional() = default; - // 20.5.4.3, assignment + /// Assignment to empty. + /// + /// Destroys the current value if there is one. optional& operator=(nullopt_t) noexcept { - clear(); + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + return *this; } - optional& operator=(const optional& rhs) { - if (initialized() == true && rhs.initialized() == false) - clear(); - else if (initialized() == false && rhs.initialized() == true) - initialize(*rhs); - else if (initialized() == true && rhs.initialized() == true) - contained_val() = *rhs; - return *this; - } + /// Copy assignment. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional& operator=(const optional& rhs) = default; - optional& operator=(optional&& rhs) noexcept(::std::is_nothrow_move_assignable::value&& ::std::is_nothrow_move_constructible::value) { - if (initialized() == true && rhs.initialized() == false) - clear(); - else if (initialized() == false && rhs.initialized() == true) - initialize(::std::move(*rhs)); - else if (initialized() == true && rhs.initialized() == true) - contained_val() = ::std::move(*rhs); - return *this; - } + /// Move assignment. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional& operator=(optional&& rhs) = default; - template - auto operator=(U&& v) - -> typename ::std::enable_if< - ::std::is_same::type, T>::value, - optional&>::type { - if (initialized()) { - contained_val() = ::std::forward(v); + /// Assigns the stored value from `u`, destroying the old value if there was + /// one. + /// \synopsis optional &operator=(U &&u); + template * = nullptr> + optional& operator=(U&& u) { + if (has_value()) { + this->m_value = std::forward(u); } else { - initialize(::std::forward(v)); + this->construct(std::forward(u)); } + return *this; } + /// Converting copy assignment operator. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + /// \synopsis optional &operator=(const optional & rhs); + template * = nullptr> + optional& operator=(const optional& rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = *rhs; + } + else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(*rhs); + } + + return *this; + } + + // TODO check exception guarantee + /// Converting move assignment operator. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + /// \synopsis optional &operator=(optional && rhs); + template * = nullptr> + optional& operator=(optional&& rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(*rhs); + } + else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + + return *this; + } + + /// Constructs the value in-place, destroying the current one if there is + /// one. + /// \group emplace template - void emplace(Args&&... args) { - clear(); - initialize(::std::forward(args)...); + T& emplace(Args&&... args) { + static_assert(std::is_constructible::value, "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward(args)...); + return value(); } + /// \group emplace + /// \synopsis template \nT& emplace(std::initializer_list il, Args &&... args); template - void emplace(::std::initializer_list il, Args&&... args) { - clear(); - initialize(il, ::std::forward(args)...); + detail::enable_if_t&, Args&&...>::value, T&> emplace(std::initializer_list il, Args&&... args) { + *this = nullopt; + this->construct(il, std::forward(args)...); + return value(); } - // 20.5.4.4, Swap - void swap(optional& rhs) noexcept(::std::is_nothrow_move_constructible::value&& noexcept(swap(::std::declval(), ::std::declval()))) { - if (initialized() == true && rhs.initialized() == false) { - rhs.initialize(::std::move(**this)); - clear(); + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible::value&& detail::is_nothrow_swappable::value) { + if (has_value()) { + if (rhs.has_value()) { + using std::swap; + swap(**this, *rhs); + } + else { + new (std::addressof(rhs.m_value)) T(std::move(this->m_value)); + this->m_value.T::~T(); + } } - else if (initialized() == false && rhs.initialized() == true) { - initialize(::std::move(*rhs)); - rhs.clear(); - } - else if (initialized() == true && rhs.initialized() == true) { - using ::std::swap; - swap(**this, *rhs); + else if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + rhs.m_value.T::~T(); } } - // 20.5.4.5, Observers - - explicit constexpr operator bool() const noexcept { - return initialized(); + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T* operator->() const { + return std::addressof(this->m_value); } - constexpr T const* operator->() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); + /// \group pointer + /// \synopsis constexpr T *operator->(); + SOL_TL_OPTIONAL_11_CONSTEXPR T* operator->() { + return std::addressof(this->m_value); } -#if OPTIONAL_HAS_MOVE_ACCESSORS == 1 - - OPTIONAL_MUTABLE_CONSTEXPR T* operator->() { - assert(initialized()); - return dataptr(); + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + SOL_TL_OPTIONAL_11_CONSTEXPR T& operator*() & { + return this->m_value; } - constexpr T const& operator*() const& { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T& operator*() const& { + return this->m_value; } - OPTIONAL_MUTABLE_CONSTEXPR T& operator*() & { - assert(initialized()); - return contained_val(); + /// \exclude + SOL_TL_OPTIONAL_11_CONSTEXPR T&& operator*() && { + return std::move(this->m_value); } - OPTIONAL_MUTABLE_CONSTEXPR T&& operator*() && { - assert(initialized()); - return constexpr_move(contained_val()); +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \exclude + constexpr const T&& operator*() const&& { + return std::move(this->m_value); } - - constexpr T const& value() const& { - return initialized() ? contained_val() -#ifdef SOL_NO_EXCEPTIONS - // we can't abort here - // because there's no constexpr abort - : *static_cast(nullptr); -#else - : (throw bad_optional_access("bad optional access"), contained_val()); -#endif - } - - OPTIONAL_MUTABLE_CONSTEXPR T& value() & { - return initialized() ? contained_val() -#ifdef SOL_NO_EXCEPTIONS - : *static_cast(nullptr); -#else - : (throw bad_optional_access("bad optional access"), contained_val()); -#endif - } - - OPTIONAL_MUTABLE_CONSTEXPR T&& value() && { - return initialized() ? contained_val() -#ifdef SOL_NO_EXCEPTIONS - // we can't abort here - // because there's no constexpr abort - : std::move(*static_cast(nullptr)); -#else - : (throw bad_optional_access("bad optional access"), contained_val()); -#endif - } - -#else - - T* operator->() { - assert(initialized()); - return dataptr(); - } - - constexpr T const& operator*() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); - } - - T& operator*() { - assert(initialized()); - return contained_val(); - } - - constexpr T const& value() const { - return initialized() ? contained_val() -#ifdef SOL_NO_EXCEPTIONS - // we can't abort here - // because there's no constexpr abort - : *static_cast(nullptr); -#else - : (throw bad_optional_access("bad optional access"), contained_val()); -#endif - } - - T& value() { - return initialized() ? contained_val() -#ifdef SOL_NO_EXCEPTIONS - // we can abort here - // but the others are constexpr, so we can't... - : (std::abort(), *static_cast(nullptr)); -#else - : (throw bad_optional_access("bad optional access"), contained_val()); -#endif - } - #endif -#if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 - - template - constexpr T value_or(V&& v) const& { - return *this ? **this : detail_::convert(constexpr_forward(v)); + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value() const noexcept { + return this->m_has_value; } -#if OPTIONAL_HAS_MOVE_ACCESSORS == 1 - - template - OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) && { - return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + /// \group has_value + constexpr explicit operator bool() const noexcept { + return this->m_has_value; } -#else - - template - T value_or(V&& v) && { - return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + /// \returns the contained value if there is one, otherwise throws + /// [bad_optional_access] + /// \group value + /// \synopsis constexpr T &value(); + SOL_TL_OPTIONAL_11_CONSTEXPR T& value() & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + SOL_TL_OPTIONAL_11_CONSTEXPR const T& value() const& { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \exclude + SOL_TL_OPTIONAL_11_CONSTEXPR T&& value() && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); } +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \exclude + SOL_TL_OPTIONAL_11_CONSTEXPR const T&& value() const&& { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } #endif -#else - - template - constexpr T value_or(V&& v) const { - return *this ? **this : detail_::convert(constexpr_forward(v)); + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template + constexpr T& value_or(U&& u) const& { + static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); } -#endif - }; + /// \group value_or + template + SOL_TL_OPTIONAL_11_CONSTEXPR T& value_or(U&& u) && { + static_assert(std::is_move_constructible::value && std::is_convertible::value, "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } + }; // namespace sol + + /// \group relop + /// \brief Compares two optional objects + /// \details If both optionals contain a value, they are compared with `T`s + /// relational operators. Otherwise `lhs` and `rhs` are equal only if they are + /// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs` + /// is not. + template + inline constexpr bool operator==(const optional& lhs, const optional& rhs) { + return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); + } + /// \group relop + template + inline constexpr bool operator!=(const optional& lhs, const optional& rhs) { + return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); + } + /// \group relop + template + inline constexpr bool operator<(const optional& lhs, const optional& rhs) { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); + } + /// \group relop + template + inline constexpr bool operator>(const optional& lhs, const optional& rhs) { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); + } + /// \group relop + template + inline constexpr bool operator<=(const optional& lhs, const optional& rhs) { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); + } + /// \group relop + template + inline constexpr bool operator>=(const optional& lhs, const optional& rhs) { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); + } + + /// \group relop_nullopt + /// \brief Compares an optional to a `nullopt` + /// \details Equivalent to comparing the optional to an empty optional + template + inline constexpr bool operator==(const optional& lhs, nullopt_t) noexcept { + return !lhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator==(nullopt_t, const optional& rhs) noexcept { + return !rhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator!=(const optional& lhs, nullopt_t) noexcept { + return lhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator!=(nullopt_t, const optional& rhs) noexcept { + return rhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator<(const optional&, nullopt_t) noexcept { + return false; + } + /// \group relop_nullopt + template + inline constexpr bool operator<(nullopt_t, const optional& rhs) noexcept { + return rhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator<=(const optional& lhs, nullopt_t) noexcept { + return !lhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator<=(nullopt_t, const optional&) noexcept { + return true; + } + /// \group relop_nullopt + template + inline constexpr bool operator>(const optional& lhs, nullopt_t) noexcept { + return lhs.has_value(); + } + /// \group relop_nullopt + template + inline constexpr bool operator>(nullopt_t, const optional&) noexcept { + return false; + } + /// \group relop_nullopt + template + inline constexpr bool operator>=(const optional&, nullopt_t) noexcept { + return true; + } + /// \group relop_nullopt + template + inline constexpr bool operator>=(nullopt_t, const optional& rhs) noexcept { + return !rhs.has_value(); + } + + /// \group relop_t + /// \brief Compares the optional with a value. + /// \details If the optional has a value, it is compared with the other value + /// using `T`s relational operators. Otherwise, the optional is considered + /// less than the value. + template + inline constexpr bool operator==(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs == rhs : false; + } + /// \group relop_t + template + inline constexpr bool operator==(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs == *rhs : false; + } + /// \group relop_t + template + inline constexpr bool operator!=(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs != rhs : true; + } + /// \group relop_t + template + inline constexpr bool operator!=(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs != *rhs : true; + } + /// \group relop_t + template + inline constexpr bool operator<(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs < rhs : true; + } + /// \group relop_t + template + inline constexpr bool operator<(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs < *rhs : false; + } + /// \group relop_t + template + inline constexpr bool operator<=(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs <= rhs : true; + } + /// \group relop_t + template + inline constexpr bool operator<=(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs <= *rhs : false; + } + /// \group relop_t + template + inline constexpr bool operator>(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs > rhs : false; + } + /// \group relop_t + template + inline constexpr bool operator>(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs > *rhs : true; + } + /// \group relop_t + template + inline constexpr bool operator>=(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs >= rhs : false; + } + /// \group relop_t + template + inline constexpr bool operator>=(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs >= *rhs : true; + } + + /// \synopsis template \nvoid swap(optional &lhs, optional &rhs); + template ::value>* = nullptr, detail::enable_if_t::value>* = nullptr> + void swap(optional& lhs, optional& rhs) noexcept(noexcept(lhs.swap(rhs))) { + return lhs.swap(rhs); + } + + namespace detail { + struct i_am_secret {}; + } // namespace detail + + template ::value, detail::decay_t, T>> + inline constexpr optional make_optional(U&& v) { + return optional(std::forward(v)); + } + + template + inline constexpr optional make_optional(Args&&... args) { + return optional(in_place, std::forward(args)...); + } + template + inline constexpr optional make_optional(std::initializer_list il, Args&&... args) { + return optional(in_place, il, std::forward(args)...); + } + +#if __cplusplus >= 201703L + template + optional(T)->optional; +#endif + + /// \exclude + namespace detail { +#ifdef SOL_TL_OPTIONAL_CXX14 + template (), *std::declval())), + detail::enable_if_t::value>* = nullptr> + constexpr auto optional_map_impl(Opt&& opt, F&& f) { + return opt.has_value() ? detail::invoke(std::forward(f), *std::forward(opt)) : optional(nullopt); + } + + template (), *std::declval())), + detail::enable_if_t::value>* = nullptr> + auto optional_map_impl(Opt&& opt, F&& f) { + if (opt.has_value()) { + detail::invoke(std::forward(f), *std::forward(opt)); + return make_optional(monostate{}); + } + + return optional(nullopt); + } +#else + template (), *std::declval())), + detail::enable_if_t::value>* = nullptr> + + constexpr auto optional_map_impl(Opt&& opt, F&& f) -> optional { + return opt.has_value() ? detail::invoke(std::forward(f), *std::forward(opt)) : optional(nullopt); + } + + template (), *std::declval())), + detail::enable_if_t::value>* = nullptr> + + auto optional_map_impl(Opt&& opt, F&& f) -> optional { + if (opt.has_value()) { + detail::invoke(std::forward(f), *std::forward(opt)); + return monostate{}; + } + + return nullopt; + } +#endif + } // namespace detail + + /// Specialization for when `T` is a reference. `optional` acts similarly + /// to a `T*`, but provides more operations and shows intent more clearly. + /// + /// *Examples*: + /// + /// ``` + /// int i = 42; + /// tl::optional o = i; + /// *o == 42; //true + /// i = 12; + /// *o = 12; //true + /// &*o == &i; //true + /// ``` + /// + /// Assignment has rebind semantics rather than assign-through semantics: + /// + /// ``` + /// int j = 8; + /// o = j; + /// + /// &*o == &j; //true + /// ``` template class optional { - static_assert(!::std::is_same::value, "bad T"); - static_assert(!::std::is_same::value, "bad T"); - T* ref; - public: - // 20.5.5.1, construction/destruction - constexpr optional() noexcept - : ref(nullptr) { +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55) + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - constexpr optional(nullopt_t) noexcept - : ref(nullptr) { + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - constexpr optional(T& v) noexcept - : ref(detail_::static_addressof(v)) { + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr auto and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - optional(T&&) = delete; +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr auto and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); - constexpr optional(const optional& rhs) noexcept - : ref(rhs.ref) { + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); + } +#endif +#else + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - explicit constexpr optional(in_place_t, T& v) noexcept - : ref(detail_::static_addressof(v)) { + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); } - explicit optional(in_place_t, T&&) = delete; + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template + constexpr detail::invoke_result_t and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template + constexpr detail::invoke_result_t and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) : result(nullopt); + } +#endif +#endif + +#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&; + template + constexpr auto map(F&& f) const& { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&&; + template + constexpr auto map(F&& f) const&& { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template auto map(F &&f) &; + template + SOL_TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F&& f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) &&; + template + SOL_TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + /// \group map + /// \synopsis template auto map(F &&f) const&; + template + constexpr decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F&& f) const& { + return detail::optional_map_impl(*this, std::forward(f)); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group map + /// \synopsis template auto map(F &&f) const&&; + template + constexpr decltype(detail::optional_map_impl(std::declval(), std::declval())) map(F&& f) const&& { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + + /// \brief Calls `f` if the optional is empty + /// \requires `std::invoke_result_t` must be void or convertible to + /// `optional`. \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// + /// \group or_else + /// \synopsis template optional or_else (F &&f) &; + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + return has_value() ? *this : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) &&; + template * = nullptr> + optional or_else(F&& f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; + template * = nullptr> + optional or_else(F&& f) const& { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& { + return has_value() ? *this : std::forward(f)(); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \exclude + template * = nullptr> + optional or_else(F&& f) const&& { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + /// \exclude + template * = nullptr> + optional or_else(F&& f) const&& { + return has_value() ? std::move(*this) : std::forward(f)(); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise returns + /// `u`. + /// + /// \details If there is a value stored, then `f` is called with `**this` + /// and the value is returned. Otherwise `u` is returned. + /// + /// \group map_or + template + U map_or(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); + } + + /// \group map_or + template + U map_or(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); + } + + /// \group map_or + template + U map_or(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group map_or + template + U map_or(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u); + } +#endif + + /// \brief Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + /// + /// \details If there is a value stored, then `f` is + /// called with `**this` and the value is returned. Otherwise + /// `std::forward(u)()` is returned. + /// + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// &&; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); + } + + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u)(); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &&; + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) : std::forward(u)(); + } +#endif + + /// \returns `u` if `*this` has a value, otherwise an empty optional. + template + constexpr optional::type> conjunction(U&& u) const { + using result = optional>; + return has_value() ? result{ u } : result{ nullopt }; + } + + /// \returns `rhs` if `*this` is empty, otherwise the current value. + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & { + return has_value() ? *this : rhs; + } + + /// \group disjunction + constexpr optional disjunction(const optional& rhs) const& { + return has_value() ? *this : rhs; + } + + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(const optional& rhs) const&& { + return has_value() ? std::move(*this) : rhs; + } +#endif + + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + constexpr optional disjunction(optional&& rhs) const& { + return has_value() ? *this : std::move(rhs); + } + + /// \group disjunction + SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group disjunction + constexpr optional disjunction(optional&& rhs) const&& { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + /// \group take + optional take() & { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() const& { + optional ret = *this; + reset(); + return ret; + } + + /// \group take + optional take() && { + optional ret = std::move(*this); + reset(); + return ret; + } + +#ifndef SOL_TL_OPTIONAL_NO_CONSTRR + /// \group take + optional take() const&& { + optional ret = std::move(*this); + reset(); + return ret; + } +#endif + + using value_type = T&; + + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept : m_value(nullptr) { + } + + /// \group ctor_empty + constexpr optional(nullopt_t) noexcept : m_value(nullptr) { + } + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + SOL_TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) noexcept = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + SOL_TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default; + + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template >::value>* = nullptr> + constexpr optional(U&& u) : m_value(std::addressof(u)) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + } + + /// \exclude + template + constexpr explicit optional(const optional& rhs) : optional(*rhs) { + } + + /// No-op ~optional() = default; - // 20.5.5.2, mutation + /// Assignment to empty. + /// + /// Destroys the current value if there is one. optional& operator=(nullopt_t) noexcept { - ref = nullptr; + m_value = nullptr; return *this; } - // optional& operator=(const optional& rhs) noexcept { - // ref = rhs.ref; - // return *this; - // } + /// Copy assignment. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + optional& operator=(const optional& rhs) = default; - // optional& operator=(optional&& rhs) noexcept { - // ref = rhs.ref; - // return *this; - // } - - template - auto operator=(U&& rhs) noexcept - -> typename ::std::enable_if< - ::std::is_same::type, optional>::value, - optional&>::type { - ref = rhs.ref; + /// Rebinds this optional to `u`. + /// + /// \requires `U` must be an lvalue reference. + /// \synopsis optional &operator=(U &&u); + template >::value>* = nullptr> + optional& operator=(U&& u) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + m_value = std::addressof(u); return *this; } - template - auto operator=(U&& rhs) noexcept - -> typename ::std::enable_if< - !::std::is_same::type, optional>::value, - optional&>::type = delete; - - void emplace(T& v) noexcept { - ref = detail_::static_addressof(v); + /// Converting copy assignment operator. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + template + optional& operator=(const optional& rhs) { + m_value = std::addressof(rhs.value()); + return *this; } - void emplace(T&&) = delete; + /// Constructs the value in-place, destroying the current one if there is + /// one. + /// + /// \group emplace + template + T& emplace(Args&&... args) noexcept { + static_assert(std::is_constructible::value, "T must be constructible with Args"); - void swap(optional& rhs) noexcept { - ::std::swap(ref, rhs.ref); + *this = nullopt; + this->construct(std::forward(args)...); } - // 20.5.5.3, observers - constexpr T* operator->() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void swap(optional& rhs) noexcept { + std::swap(m_value, rhs.m_value); } - constexpr T& operator*() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T* operator->() const { + return m_value; } - constexpr T& value() const { -#ifdef SOL_NO_EXCEPTIONS - return *ref; -#else - return ref ? *ref - : (throw bad_optional_access("bad optional access"), *ref); -#endif // Exceptions + /// \group pointer + /// \synopsis constexpr T *operator->(); + SOL_TL_OPTIONAL_11_CONSTEXPR T* operator->() { + return m_value; } - explicit constexpr operator bool() const noexcept { - return ref != nullptr; + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + SOL_TL_OPTIONAL_11_CONSTEXPR T& operator*() { + return *m_value; } - template - constexpr T& value_or(V&& v) const { - return *this ? **this : detail_::convert(constexpr_forward(v)); + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T& operator*() const { + return *m_value; } + + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool has_value() const noexcept { + return m_value != nullptr; + } + + /// \group has_value + constexpr explicit operator bool() const noexcept { + return m_value != nullptr; + } + + /// \returns the contained value if there is one, otherwise throws + /// [bad_optional_access] + /// \group value + /// synopsis constexpr T &value(); + SOL_TL_OPTIONAL_11_CONSTEXPR T& value() { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + SOL_TL_OPTIONAL_11_CONSTEXPR const T& value() const { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template + constexpr T& value_or(U&& u) const& { + static_assert(std::is_copy_constructible::value && std::is_convertible::value, "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// \group value_or + template + SOL_TL_OPTIONAL_11_CONSTEXPR T& value_or(U&& u) && { + static_assert(std::is_move_constructible::value && std::is_convertible::value, "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { + m_value = nullptr; + } + + private: + T* m_value; }; - template - class optional { - static_assert(sizeof(T) == 0, "optional rvalue references disallowed"); - }; - - // 20.5.8, Relational operators - template - constexpr bool operator==(const optional& x, const optional& y) { - return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; - } - - template - constexpr bool operator!=(const optional& x, const optional& y) { - return !(x == y); - } - - template - constexpr bool operator<(const optional& x, const optional& y) { - return (!y) ? false : (!x) ? true : *x < *y; - } - - template - constexpr bool operator>(const optional& x, const optional& y) { - return (y < x); - } - - template - constexpr bool operator<=(const optional& x, const optional& y) { - return !(y < x); - } - - template - constexpr bool operator>=(const optional& x, const optional& y) { - return !(x < y); - } - - // 20.5.9, Comparison with nullopt - template - constexpr bool operator==(const optional& x, nullopt_t) noexcept { - return (!x); - } - - template - constexpr bool operator==(nullopt_t, const optional& x) noexcept { - return (!x); - } - - template - constexpr bool operator!=(const optional& x, nullopt_t) noexcept { - return bool(x); - } - - template - constexpr bool operator!=(nullopt_t, const optional& x) noexcept { - return bool(x); - } - - template - constexpr bool operator<(const optional&, nullopt_t) noexcept { - return false; - } - - template - constexpr bool operator<(nullopt_t, const optional& x) noexcept { - return bool(x); - } - - template - constexpr bool operator<=(const optional& x, nullopt_t) noexcept { - return (!x); - } - - template - constexpr bool operator<=(nullopt_t, const optional&) noexcept { - return true; - } - - template - constexpr bool operator>(const optional& x, nullopt_t) noexcept { - return bool(x); - } - - template - constexpr bool operator>(nullopt_t, const optional&) noexcept { - return false; - } - - template - constexpr bool operator>=(const optional&, nullopt_t) noexcept { - return true; - } - - template - constexpr bool operator>=(nullopt_t, const optional& x) noexcept { - return (!x); - } - - // 20.5.10, Comparison with T - template - constexpr bool operator==(const optional& x, const T& v) { - return bool(x) ? *x == v : false; - } - - template - constexpr bool operator==(const T& v, const optional& x) { - return bool(x) ? v == *x : false; - } - - template - constexpr bool operator!=(const optional& x, const T& v) { - return bool(x) ? *x != v : true; - } - - template - constexpr bool operator!=(const T& v, const optional& x) { - return bool(x) ? v != *x : true; - } - - template - constexpr bool operator<(const optional& x, const T& v) { - return bool(x) ? *x < v : true; - } - - template - constexpr bool operator>(const T& v, const optional& x) { - return bool(x) ? v > *x : true; - } - - template - constexpr bool operator>(const optional& x, const T& v) { - return bool(x) ? *x > v : false; - } - - template - constexpr bool operator<(const T& v, const optional& x) { - return bool(x) ? v < *x : false; - } - - template - constexpr bool operator>=(const optional& x, const T& v) { - return bool(x) ? *x >= v : false; - } - - template - constexpr bool operator<=(const T& v, const optional& x) { - return bool(x) ? v <= *x : false; - } - - template - constexpr bool operator<=(const optional& x, const T& v) { - return bool(x) ? *x <= v : true; - } - - template - constexpr bool operator>=(const T& v, const optional& x) { - return bool(x) ? v >= *x : true; - } - - // Comparison of optional with T - template - constexpr bool operator==(const optional& x, const T& v) { - return bool(x) ? *x == v : false; - } - - template - constexpr bool operator==(const T& v, const optional& x) { - return bool(x) ? v == *x : false; - } - - template - constexpr bool operator!=(const optional& x, const T& v) { - return bool(x) ? *x != v : true; - } - - template - constexpr bool operator!=(const T& v, const optional& x) { - return bool(x) ? v != *x : true; - } - - template - constexpr bool operator<(const optional& x, const T& v) { - return bool(x) ? *x < v : true; - } - - template - constexpr bool operator>(const T& v, const optional& x) { - return bool(x) ? v > *x : true; - } - - template - constexpr bool operator>(const optional& x, const T& v) { - return bool(x) ? *x > v : false; - } - - template - constexpr bool operator<(const T& v, const optional& x) { - return bool(x) ? v < *x : false; - } - - template - constexpr bool operator>=(const optional& x, const T& v) { - return bool(x) ? *x >= v : false; - } - - template - constexpr bool operator<=(const T& v, const optional& x) { - return bool(x) ? v <= *x : false; - } - - template - constexpr bool operator<=(const optional& x, const T& v) { - return bool(x) ? *x <= v : true; - } - - template - constexpr bool operator>=(const T& v, const optional& x) { - return bool(x) ? v >= *x : true; - } - - // Comparison of optional with T - template - constexpr bool operator==(const optional& x, const T& v) { - return bool(x) ? *x == v : false; - } - - template - constexpr bool operator==(const T& v, const optional& x) { - return bool(x) ? v == *x : false; - } - - template - constexpr bool operator!=(const optional& x, const T& v) { - return bool(x) ? *x != v : true; - } - - template - constexpr bool operator!=(const T& v, const optional& x) { - return bool(x) ? v != *x : true; - } - - template - constexpr bool operator<(const optional& x, const T& v) { - return bool(x) ? *x < v : true; - } - - template - constexpr bool operator>(const T& v, const optional& x) { - return bool(x) ? v > *x : true; - } - - template - constexpr bool operator>(const optional& x, const T& v) { - return bool(x) ? *x > v : false; - } - - template - constexpr bool operator<(const T& v, const optional& x) { - return bool(x) ? v < *x : false; - } - - template - constexpr bool operator>=(const optional& x, const T& v) { - return bool(x) ? *x >= v : false; - } - - template - constexpr bool operator<=(const T& v, const optional& x) { - return bool(x) ? v <= *x : false; - } - - template - constexpr bool operator<=(const optional& x, const T& v) { - return bool(x) ? *x <= v : true; - } - - template - constexpr bool operator>=(const T& v, const optional& x) { - return bool(x) ? v >= *x : true; - } - - // 20.5.12, Specialized algorithms - template - void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) { - x.swap(y); - } - - template - constexpr optional::type> make_optional(T&& v) { - return optional::type>(constexpr_forward(v)); - } - - template - constexpr optional make_optional(::std::reference_wrapper v) { - return optional(v.get()); - } - } // namespace sol namespace std { - template - struct hash> { - typedef typename hash::result_type result_type; - typedef sol::optional argument_type; + // TODO SFINAE + template + struct hash< ::sol::optional > { + ::std::size_t operator()(const ::sol::optional& o) const { + if (!o.has_value()) + return 0; - constexpr result_type operator()(argument_type const& arg) const { - return arg ? ::std::hash{}(*arg) : result_type{}; - } - }; - - template - struct hash> { - typedef typename hash::result_type result_type; - typedef sol::optional argument_type; - - constexpr result_type operator()(argument_type const& arg) const { - return arg ? ::std::hash{}(*arg) : result_type{}; + return ::std::hash< ::sol::detail::remove_const_t>()(*o); } }; } // namespace std -#if defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ -#pragma warning(pop) #endif - -#undef TR2_OPTIONAL_REQUIRES -#undef TR2_OPTIONAL_ASSERTED_EXPRESSION - -#endif // SOL_OPTIONAL_IMPLEMENTATION_HPP diff --git a/include/sol/protected_function.hpp b/include/sol/protected_function.hpp index 2059b9ab..af393edd 100644 --- a/include/sol/protected_function.hpp +++ b/include/sol/protected_function.hpp @@ -33,6 +33,7 @@ #include namespace sol { + template class basic_protected_function : public base_t { public: diff --git a/include/sol/sol.hpp b/include/sol/sol.hpp index 5965dffd..bb0d21aa 100644 --- a/include/sol/sol.hpp +++ b/include/sol/sol.hpp @@ -44,17 +44,21 @@ // we'll just let this alone for now #elif defined _MSC_VER #pragma warning(push) -#pragma warning(disable : 4324) // structure was padded due to alignment specifier -#pragma warning(disable : 4503) // decorated name horse shit -#pragma warning(disable : 4702) // unreachable code -#pragma warning(disable : 4127) // 'conditional expression is constant' yeah that's the point your old compilers don't have `if constexpr` you jerk -#pragma warning(disable : 4505) // some other nonsense warning +//#pragma warning(disable : 4324) // structure was padded due to alignment specifier +//#pragma warning(disable : 4503) // decorated name horse shit +//#pragma warning(disable : 4702) // unreachable code +//#pragma warning(disable : 4127) // 'conditional expression is constant' yeah that's the point your old compilers don't have `if constexpr` you jerk +//#pragma warning(disable : 4505) // some other nonsense warning #endif // clang++ vs. g++ vs. VC++ #include "forward.hpp" +#include "forward_detail.hpp" +#include "stack.hpp" #include "object.hpp" #include "function.hpp" #include "protected_function.hpp" +#include "usertype.hpp" +#include "table.hpp" #include "state.hpp" #include "coroutine.hpp" #include "thread.hpp" diff --git a/include/sol/stack.hpp b/include/sol/stack.hpp index 1cf5f81e..7f1365ab 100644 --- a/include/sol/stack.hpp +++ b/include/sol/stack.hpp @@ -69,6 +69,21 @@ namespace sol { return chunkname.c_str(); } } + + inline void clear_entries(stack_reference r) { + stack::push(r.lua_state(), lua_nil); + while (lua_next(r.lua_state(), -2)) { + absolute_index key(r.lua_state(), -2); + auto pn = stack::pop_n(r.lua_state(), 1); + stack::set_field(r.lua_state(), key, lua_nil, r.stack_index()); + } + } + + inline void clear_entries(const reference& registry_reference) { + auto pp = stack::push_pop(registry_reference); + stack_reference ref(registry_reference.lua_state(), -1); + clear_entries(ref); + } } // namespace detail namespace stack { @@ -189,13 +204,9 @@ namespace sol { template >::value>> inline int call_into_lua(types, types ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) { decltype(auto) r = call(types>(), ta, L, start, std::forward(fx), std::forward(fxargs)...); - typedef meta::unqualified_t R; - typedef meta::any, - std::is_same, - std::is_same, - std::is_same> - is_stack; - if (clean_stack && !is_stack::value) { + using R = meta::unqualified_t; + using is_stack = meta::any, std::is_same, std::is_same, std::is_same>; + if constexpr (clean_stack && !is_stack::value) { lua_settop(L, 0); } return push_reference(L, std::forward(r)); diff --git a/include/sol/stack_check_unqualified.hpp b/include/sol/stack_check_unqualified.hpp index 8cd539a2..3d5b7689 100644 --- a/include/sol/stack_check_unqualified.hpp +++ b/include/sol/stack_check_unqualified.hpp @@ -479,7 +479,8 @@ namespace sol { namespace stack { if (stack_detail::check_metatable>(L, metatableindex)) return true; bool success = false; - if (derive::value || weak_derive::value) { + bool has_derived = derive::value || weak_derive::value; + if (has_derived) { #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 1, detail::not_enough_stack_space_string); #endif // make sure stack doesn't overflow diff --git a/include/sol/stack_core.hpp b/include/sol/stack_core.hpp index 90bdd561..1da19274 100644 --- a/include/sol/stack_core.hpp +++ b/include/sol/stack_core.hpp @@ -1244,7 +1244,9 @@ namespace sol { else if constexpr (!std::is_pointer_v) { return &usertype_alloc_destruct; } - return &cannot_destruct; + else { + return &cannot_destruct; + } } template @@ -1321,27 +1323,30 @@ namespace sol { template int comparsion_operator_wrap(lua_State* L) { auto maybel = stack::unqualified_check_get(L, 1); - if (maybel) { - auto mayber = stack::unqualified_check_get(L, 2); - if (mayber) { - auto& l = *maybel; - auto& r = *mayber; - if constexpr (std::is_same_v, Op> // cf-hack - || std::is_same_v, Op> // - || std::is_same_v, Op>) { // - if (detail::ptr(l) == detail::ptr(r)) { - return stack::push(L, true); - } - } - else if constexpr (std::is_same_v) { - std::equal_to<> op; - return stack::push(L, op(detail::ptr(l), detail::ptr(r))); - } - Op op; - return stack::push(L, op(detail::deref(l), detail::deref(r))); - } + if (!maybel) { + return stack::push(L, false); + } + auto mayber = stack::unqualified_check_get(L, 2); + if (!mayber) { + return stack::push(L, false); + } + decltype(auto) l = *maybel; + decltype(auto) r = *mayber; + if constexpr (std::is_same_v) { + std::equal_to<> op; + return stack::push(L, op(detail::ptr(l), detail::ptr(r))); + } + else { + if constexpr (std::is_same_v, Op> // cf-hack + || std::is_same_v, Op> // + || std::is_same_v, Op>) { // + if (detail::ptr(l) == detail::ptr(r)) { + return stack::push(L, true); + } + } + Op op; + return stack::push(L, op(detail::deref(l), detail::deref(r))); } - return stack::push(L, false); } template diff --git a/include/sol/stack_get_unqualified.hpp b/include/sol/stack_get_unqualified.hpp index c613ccdb..ba594fe8 100644 --- a/include/sol/stack_get_unqualified.hpp +++ b/include/sol/stack_get_unqualified.hpp @@ -791,7 +791,8 @@ namespace sol { namespace stack { } static T* get_no_lua_nil_from(lua_State* L, void* udata, int index, record&) { - if (derive::value || weak_derive::value) { + bool has_derived = derive::value || weak_derive::value; + if (has_derived) { if (lua_getmetatable(L, index) == 1) { lua_getfield(L, -1, &detail::base_class_cast_key()[0]); if (type_of(L, -1) != type::lua_nil) { diff --git a/include/sol/stack_probe.hpp b/include/sol/stack_probe.hpp index 726b864b..a5a3ce24 100644 --- a/include/sol/stack_probe.hpp +++ b/include/sol/stack_probe.hpp @@ -34,8 +34,10 @@ namespace stack { struct probe_field_getter { template probe get(lua_State* L, Key&& key, int tableindex = -2) { - if (!b && !maybe_indexable(L, tableindex)) { - return probe(false, 0); + if constexpr(!b) { + if (!maybe_indexable(L, tableindex)) { + return probe(false, 0); + } } get_field(L, std::forward(key), tableindex); return probe(check

(L), 1); diff --git a/include/sol/stack_push.hpp b/include/sol/stack_push.hpp index 912430fd..11d0c1ef 100644 --- a/include/sol/stack_push.hpp +++ b/include/sol/stack_push.hpp @@ -46,6 +46,23 @@ namespace sol { namespace stack { + namespace stack_detail { + template + inline bool integer_value_fits(const T& value) { + if constexpr (sizeof(T) < sizeof(lua_Integer) || (std::is_signed::value && sizeof(T) == sizeof(lua_Integer))) { + (void)value; + return true; + } + else { + auto u_min = static_cast((std::numeric_limits::min)()); + auto u_max = static_cast((std::numeric_limits::max)()); + auto t_min = static_cast((std::numeric_limits::min)()); + auto t_max = static_cast((std::numeric_limits::max)()); + return (u_min <= t_min || value >= static_cast(u_min)) && (u_max >= t_max || value <= static_cast(u_max)); + } + } + } + inline int push_environment_of(lua_State* L, int index = -1) { #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 1, detail::not_enough_stack_space_environment); @@ -241,17 +258,7 @@ namespace sol { luaL_checkstack(L, 1, detail::not_enough_stack_space_integral); #endif // make sure stack doesn't overflow #if SOL_LUA_VERSION >= 503 - static auto integer_value_fits = [](T const& value) { - if (sizeof(T) < sizeof(lua_Integer) || (std::is_signed::value && sizeof(T) == sizeof(lua_Integer))) { - return true; - } - auto u_min = static_cast((std::numeric_limits::min)()); - auto u_max = static_cast((std::numeric_limits::max)()); - auto t_min = static_cast((std::numeric_limits::min)()); - auto t_max = static_cast((std::numeric_limits::max)()); - return (u_min <= t_min || value >= static_cast(u_min)) && (u_max >= t_max || value <= static_cast(u_max)); - }; - if (integer_value_fits(value)) { + if (stack_detail::integer_value_fits(value)) { lua_pushinteger(L, static_cast(value)); return 1; } @@ -813,7 +820,7 @@ namespace sol { } static int push(lua_State* L, const wchar_t* strb, const wchar_t* stre) { - if (sizeof(wchar_t) == 2) { + if constexpr (sizeof(wchar_t) == 2) { const char16_t* sb = reinterpret_cast(strb); const char16_t* se = reinterpret_cast(stre); return stack::push(L, sb, se); diff --git a/include/sol/string_view.hpp b/include/sol/string_view.hpp index d34d8eab..46d726fd 100644 --- a/include/sol/string_view.hpp +++ b/include/sol/string_view.hpp @@ -53,11 +53,9 @@ namespace sol { basic_string_view(const std::string& r) : basic_string_view(r.data(), r.size()) { } - basic_string_view(const Char* ptr) - : basic_string_view(ptr, Traits::length(ptr)) { + constexpr basic_string_view(const Char* ptr) : basic_string_view(ptr, Traits::length(ptr)) { } - basic_string_view(const Char* ptr, std::size_t sz) - : s(sz), p(ptr) { + constexpr basic_string_view(const Char* ptr, std::size_t sz) : s(sz), p(ptr) { } static int compare(const Char* lhs_p, std::size_t lhs_sz, const Char* rhs_p, std::size_t rhs_sz) { @@ -71,31 +69,31 @@ namespace sol { return 0; } - const Char* begin() const { + constexpr const Char* begin() const { return p; } - const Char* end() const { + constexpr const Char* end() const { return p + s; } - const Char* cbegin() const { + constexpr const Char* cbegin() const { return p; } - const Char* cend() const { + constexpr const Char* cend() const { return p + s; } - const Char* data() const { + constexpr const Char* data() const { return p; } - std::size_t size() const { + constexpr std::size_t size() const { return s; } - std::size_t length() const { + constexpr std::size_t length() const { return size(); } diff --git a/include/sol/table_core.hpp b/include/sol/table_core.hpp index 4bd94981..83d8ca36 100644 --- a/include/sol/table_core.hpp +++ b/include/sol/table_core.hpp @@ -66,31 +66,6 @@ namespace sol { template using is_global = meta::all, meta::is_c_str...>; - template - void for_each(std::true_type, Fx&& fx) const { - auto pp = stack::push_pop(*this); - stack::push(base_t::lua_state(), lua_nil); - while (lua_next(base_t::lua_state(), -2)) { - object key(base_t::lua_state(), -2); - object value(base_t::lua_state(), -1); - std::pair keyvalue(key, value); - auto pn = stack::pop_n(base_t::lua_state(), 1); - fx(keyvalue); - } - } - - template - void for_each(std::false_type, Fx&& fx) const { - auto pp = stack::push_pop(*this); - stack::push(base_t::lua_state(), lua_nil); - while (lua_next(base_t::lua_state(), -2)) { - object key(base_t::lua_state(), -2); - object value(base_t::lua_state(), -1); - auto pn = stack::pop_n(base_t::lua_state(), 1); - fx(key, value); - } - } - template auto tuple_get(types, std::index_sequence<0, 1, I...>, Keys&& keys) const -> decltype(stack::pop>(nullptr)) { @@ -384,7 +359,7 @@ namespace sol { for (const auto& kvp : items) { target.set(kvp.first, kvp.second); } - if (read_only) { + if constexpr (read_only) { table x = create_with(meta_function::new_index, detail::fail_on_newindex, meta_function::index, target); table shim = create_named(name, metatable_key, x); return shim; @@ -395,10 +370,29 @@ namespace sol { } } - template + template void for_each(Fx&& fx) const { - typedef meta::is_invokable)> is_paired; - for_each(is_paired(), std::forward(fx)); + if constexpr (std::is_invocable_v) { + auto pp = stack::push_pop(*this); + stack::push(base_t::lua_state(), lua_nil); + while (lua_next(base_t::lua_state(), -2)) { + Key key(base_t::lua_state(), -2); + Value value(base_t::lua_state(), -1); + auto pn = stack::pop_n(base_t::lua_state(), 1); + fx(key, value); + } + } + else { + auto pp = stack::push_pop(*this); + stack::push(base_t::lua_state(), lua_nil); + while (lua_next(base_t::lua_state(), -2)) { + Key key(base_t::lua_state(), -2); + Value value(base_t::lua_state(), -1); + auto pn = stack::pop_n(base_t::lua_state(), 1); + std::pair keyvalue(key, value); + fx(keyvalue); + } + } } size_t size() const { diff --git a/include/sol/types.hpp b/include/sol/types.hpp index ac51b0af..7db296a6 100644 --- a/include/sol/types.hpp +++ b/include/sol/types.hpp @@ -800,9 +800,14 @@ namespace sol { || std::is_base_of>::value> {}; template - struct is_lua_reference_or_proxy : std::integral_constant>::value - || meta::is_specialization_of, proxy>::value> {}; + inline constexpr bool is_lua_reference_v = is_lua_reference::value; + + template + struct is_lua_reference_or_proxy + : std::integral_constant>::value || meta::is_specialization_of, proxy>::value> {}; + + template + inline constexpr bool is_lua_reference_or_proxy_v = is_lua_reference_or_proxy::value; template struct is_transparent_argument : std::false_type {}; diff --git a/include/sol/usertype_storage.hpp b/include/sol/usertype_storage.hpp index b74529b8..04ff5635 100644 --- a/include/sol/usertype_storage.hpp +++ b/include/sol/usertype_storage.hpp @@ -44,6 +44,8 @@ namespace sol { namespace u_detail { usertype_storage& get_usertype_storage(lua_State* L); using index_call_function = int(lua_State*, void*); + using change_indexing_mem_func + = void (usertype_storage_base::*)(lua_State*, submetatable_type, void*, stack_reference&, lua_CFunction, lua_CFunction, lua_CFunction, lua_CFunction); struct index_call_storage { index_call_function* index; @@ -51,6 +53,10 @@ namespace sol { namespace u_detail { void* binding_data; }; + struct new_index_call_storage : index_call_storage { + void* new_binding_data; + }; + struct binding_base { virtual void* data() = 0; virtual ~binding_base() { @@ -148,6 +154,130 @@ namespace sol { namespace u_detail { return new_index_fail(L); } + struct string_for_each_metatable_func { + bool is_destruction = false; + bool is_index = false; + bool is_new_index = false; + bool poison_indexing = false; + bool is_unqualified_lua_CFunction = false; + bool is_unqualified_lua_reference = false; + std::string* p_key = nullptr; + reference* p_binding_ref = nullptr; + lua_CFunction call_func = nullptr; + index_call_storage* p_ics = nullptr; + usertype_storage_base* p_usb = nullptr; + void* p_derived_usb = nullptr; + lua_CFunction idx_call = nullptr, + new_idx_call = nullptr, + meta_idx_call = nullptr, + meta_new_idx_call = nullptr; + change_indexing_mem_func change_indexing; + + void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) { + std::string& key = *p_key; + usertype_storage_base& usb = *p_usb; + index_call_storage& ics = *p_ics; + + if (smt == submetatable_type::named) { + // do not override __call or + // other specific meta functions on named metatable: + // we need that for call construction + // and other amenities + return; + } + int fast_index_table_push = fast_index_table.push(); + stack_reference t(L, -fast_index_table_push); + if (poison_indexing) { + (usb.*change_indexing)(L, + smt, + p_derived_usb, + t, + idx_call, + new_idx_call, + meta_idx_call, + meta_new_idx_call); + } + if (is_destruction + && (smt == submetatable_type::reference || smt == submetatable_type::const_reference || smt == submetatable_type::named + || smt == submetatable_type::unique)) { + // gc does not apply to us here + // for reference types (raw T*, std::ref) + // for the named metatable itself, + // or for unique_usertypes, which do their own custom destruction + t.pop(); + return; + } + if (is_index || is_new_index) { + // do not serialize the new_index and index functions here directly + // we control those... + t.pop(); + return; + } + if (is_unqualified_lua_CFunction) { + stack::set_field(L, key, call_func, t.stack_index()); + } + else if (is_unqualified_lua_reference) { + reference& binding_ref = *p_binding_ref; + stack::set_field(L, key, binding_ref, t.stack_index()); + } + else { + stack::set_field(L, key, make_closure(call_func, nullptr, ics.binding_data), t.stack_index()); + } + t.pop(); + } + }; + + struct lua_reference_func { + reference key; + reference value; + + void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) { + if (smt == submetatable_type::named) { + return; + } + int fast_index_table_push = fast_index_table.push(); + stack_reference t(L, -fast_index_table_push); + stack::set_field(L, key, value, t.stack_index()); + t.pop(); + } + }; + + struct update_bases_func { + detail::inheritance_check_function base_class_check_func; + detail::inheritance_cast_function base_class_cast_func; + lua_CFunction idx_call, new_idx_call, meta_idx_call, meta_new_idx_call; + usertype_storage_base* p_usb; + void* p_derived_usb; + change_indexing_mem_func change_indexing; + + void operator()(lua_State* L, submetatable_type smt, reference& fast_index_table) { + int fast_index_table_push = fast_index_table.push(); + stack_reference t(L, -fast_index_table_push); + stack::set_field(L, detail::base_class_check_key(), reinterpret_cast(base_class_check_func), t.stack_index()); + stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast(base_class_cast_func), t.stack_index()); + // change indexing, forcefully + (p_usb->*change_indexing)(L, + smt, + p_derived_usb, + t, + idx_call, + new_idx_call, + meta_idx_call, + meta_new_idx_call); + t.pop(); + } + }; + + struct binding_data_equals { + void* binding_data; + + binding_data_equals(void* b) : binding_data(b) {} + + bool operator()(const std::unique_ptr& ptr) const { + return binding_data == ptr->data(); + } + }; + struct usertype_storage_base { public: std::vector> storage; @@ -163,13 +293,10 @@ namespace sol { namespace u_detail { reference type_table; reference gc_names_table; reference named_metatable; - std::bitset<64> properties; - index_call_storage base_index; - index_call_storage base_new_index; - lua_CFunction weak_base_index; - lua_CFunction weak_base_new_index; + new_index_call_storage base_index; bool is_using_index; bool is_using_new_index; + std::bitset<64> properties; usertype_storage_base(lua_State* L) : storage() @@ -182,17 +309,14 @@ namespace sol { namespace u_detail { , type_table(make_reference(L, create)) , gc_names_table(make_reference(L, create)) , named_metatable(make_reference(L, create)) - , properties() , base_index() - , base_new_index() , is_using_index(false) - , is_using_new_index(false) { + , is_using_new_index(false) + , properties() { base_index.binding_data = nullptr; base_index.index = index_target_fail; - base_index.new_index = index_target_fail; - base_new_index.binding_data = nullptr; - base_new_index.index = new_index_target_fail; - base_new_index.new_index = new_index_target_fail; + base_index.new_index = new_index_target_fail; + base_index.new_binding_data = nullptr; } template @@ -242,7 +366,7 @@ namespace sol { namespace u_detail { "The size of this data pointer is too small to fit the inheritance checking function: Please file " "a bug report."); static_assert(!meta::any_same::value, "base classes cannot list the original class as part of the bases"); - if (sizeof...(Bases) < 1) { + if constexpr (sizeof...(Bases) < 1) { return; } @@ -250,31 +374,18 @@ namespace sol { namespace u_detail { void* derived_this = static_cast(static_cast*>(this)); - auto change_indexing_and_set_weak_derive = [&](lua_State* L, submetatable_type smt, reference& fast_index_table) { - int fast_index_table_push = fast_index_table.push(); - stack_reference t(L, -fast_index_table_push); - static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), - "The size of this data pointer is too small to fit the inheritance checking function: file a " - "bug report."); - static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), - "The size of this data pointer is too small to fit the inheritance checking function: file a " - "bug report."); - auto base_class_check_func = &detail::inheritance::template type_check_with; - auto base_class_cast_func = &detail::inheritance::template type_cast_with; - stack::set_field(L, detail::base_class_check_key(), reinterpret_cast(base_class_check_func), t.stack_index()); - stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast(base_class_cast_func), t.stack_index()); - // change indexing, forcefully - change_indexing(L, - smt, - derived_this, - t, - &usertype_storage::template index_call_with_bases, - &usertype_storage::template new_index_call_with_bases, - &usertype_storage::template meta_index_call_with_bases, - &usertype_storage::template meta_new_index_call_with_bases); - t.pop(); - }; - this->for_each_table(L, change_indexing_and_set_weak_derive); + update_bases_func for_each_fx; + for_each_fx.base_class_check_func = &detail::inheritance::template type_check_with; + for_each_fx.base_class_cast_func = &detail::inheritance::template type_cast_with; + for_each_fx.idx_call = &usertype_storage::template index_call_with_bases; + for_each_fx.new_idx_call = &usertype_storage::template index_call_with_bases; + for_each_fx.meta_idx_call = &usertype_storage::template meta_index_call_with_bases; + for_each_fx.meta_new_idx_call = &usertype_storage::template meta_index_call_with_bases; + for_each_fx.p_usb = this; + for_each_fx.p_derived_usb = derived_this; + for_each_fx.change_indexing = &usertype_storage_base::change_indexing; + for_each_fx.p_derived_usb = derived_this; + this->for_each_table(L, for_each_fx); } void clear() { @@ -286,7 +397,7 @@ namespace sol { namespace u_detail { // then replace unqualified_getter/setter } - template + template static void base_walk_index(lua_State* L, usertype_storage_base& self, bool& keep_going, int& base_result) { using bases = typename base::type; if (!keep_going) { @@ -296,25 +407,11 @@ namespace sol { namespace u_detail { (void)self; // TODO: get base table, dump it out usertype_storage_base& base_storage = get_usertype_storage(L); - base_result = self_index_call(bases(), L, base_storage); + base_result = self_index_call(bases(), L, base_storage); keep_going = base_result == base_walking_failed_index; } - template - static void base_walk_new_index(lua_State* L, usertype_storage_base& self, bool& keep_going, int& base_result) { - using bases = typename base::type; - if (!keep_going) { - return; - } - (void)L; - (void)self; - // TODO: get base table, dump it out - usertype_storage_base& base_storage = get_usertype_storage(L); - base_result = self_new_index_call(bases(), L, base_storage); - keep_going = base_result == base_walking_failed_index; - } - - template + template static inline int self_index_call(types, lua_State* L, usertype_storage_base& self) { type k_type = stack::get(L, 2); if (k_type == type::string) { @@ -328,7 +425,12 @@ namespace sol { namespace u_detail { } if (target != nullptr) { // let the target decide what to do - return (target->index)(L, target->binding_data); + if constexpr (is_new_index) { + return (target->new_index)(L, target->binding_data); + } + else { + return (target->index)(L, target->binding_data); + } } } else if (k_type != type::nil && k_type != type::none) { @@ -341,96 +443,63 @@ namespace sol { namespace u_detail { } } if (target != nullptr) { - // push target to return - // what we found - return stack::push(L, *target); + if constexpr(is_new_index) { + // set value and return + *target = reference(L, 3); + return 0; + } + else { + // push target to return + // what we found + return stack::push(L, *target); + } } } // retrieve bases and walk through them. bool keep_going = true; int base_result; - detail::swallow{ 1, (base_walk_index(L, self, keep_going, base_result), 1)... }; - if (sizeof...(Bases) > 0 && !keep_going) { - return base_result; + (void)keep_going; + (void)base_result; + detail::swallow{ 1, (base_walk_index(L, self, keep_going, base_result), 1)... }; + if constexpr (sizeof...(Bases) > 0) { + if (!keep_going) { + return base_result; + } } - if (base_walking) { + if constexpr (base_walking) { // if we're JUST base-walking then don't index-fail, just // return the false bits return base_walking_failed_index; } - if constexpr (from_named_metatable) { - return index_fail(L); - } - else { - return self.base_index.index(L, self.base_index.binding_data); - } - } - - template - static inline int self_new_index_call(types, lua_State* L, usertype_storage_base& self) { - type k_type = stack::get(L, 2); - if (k_type == type::string) { - index_call_storage* target = nullptr; - { - string_view k = stack::get(L, 2); - auto it = self.string_keys.find(k); - if (it != self.string_keys.cend()) { - target = &it->second; - } - } - if (target != nullptr) { - // set value through - // new_index call, whatever that entails, - // and return - return (target->new_index)(L, target->binding_data); - } - } - else if (k_type != type::nil && k_type != type::none) { - reference* target = nullptr; - { - stack_reference k = stack::get(L, 2); - auto it = self.auxiliary_keys.find(k); - if (it != self.auxiliary_keys.cend()) { - target = &it->second; - } - } - if (target != nullptr) { - // set value and return - *target = reference(L, 3); + else if constexpr (from_named_metatable) { + if constexpr (is_new_index) { + self.set(L, reference(L, raw_index(2)), reference(L, raw_index(3))); return 0; } - } - - // retrieve bases and walk through them. - bool keep_going = true; - int base_result; - detail::swallow{ 1, (base_walk_new_index(L, self, keep_going, base_result), 1)... }; - if (sizeof...(Bases) > 0 && !keep_going) { - return base_result; - } - if (base_walking) { - // if we're JUST base-walking then don't index-fail, just - // return the false bits - return base_walking_failed_index; - } - if constexpr (from_named_metatable) { - self.set(L, reference(L, raw_index(2)), reference(L, raw_index(3))); - return 0; + else { + return index_fail(L); + } } else { - return self.base_new_index.new_index(L, self.base_new_index.binding_data); + if constexpr (is_new_index) { + return self.base_index.new_index(L, self.base_index.new_binding_data); + } + else { + return self.base_index.index(L, self.base_index.binding_data); + } } } - void change_indexing(lua_State* L, submetatable_type submetatable_type, void* derived_this, stack_reference& t, lua_CFunction index, + void change_indexing(lua_State* L, submetatable_type submetatable, void* derived_this, stack_reference& t, lua_CFunction index, lua_CFunction new_index, lua_CFunction meta_index, lua_CFunction meta_new_index) { usertype_storage_base& this_base = *this; void* base_this = static_cast(&this_base); this->is_using_index |= true; this->is_using_new_index |= true; - if (submetatable_type == submetatable_type::named) { + //detail::clear_entries(t); + if (submetatable == submetatable_type::named) { stack::set_field(L, metatable_key, named_index_table, t.stack_index()); stack_reference stack_metametatable(L, -named_metatable.push()); stack::set_field(L, @@ -460,68 +529,38 @@ namespace sol { namespace u_detail { using usertype_storage_base::usertype_storage_base; - template + template static inline int index_call_(lua_State* L) { using bases = typename base::type; usertype_storage_base& self = stack::get>(L, upvalue_index(usertype_storage_index)); - return self_index_call(bases(), L, self); + return self_index_call(bases(), L, self); } - template + template static inline int index_call_with_bases_(lua_State* L) { using bases = types; usertype_storage_base& self = stack::get>(L, upvalue_index(usertype_storage_index)); - return self_index_call(bases(), L, self); - } - - template - static inline int new_index_call_(lua_State* L) { - using bases = typename base::type; - usertype_storage_base& self = stack::get>(L, upvalue_index(usertype_storage_index)); - return self_new_index_call(bases(), L, self); - } - - template - static inline int new_index_call_with_bases_(lua_State* L) { - using bases = types; - usertype_storage_base& self = stack::get>(L, upvalue_index(usertype_storage_index)); - return self_new_index_call(bases(), L, self); + return self_index_call(bases(), L, self); } + template static inline int index_call(lua_State* L) { - return detail::static_trampoline<&index_call_>(L); + return detail::static_trampoline<&index_call_>(L); } - template + template static inline int index_call_with_bases(lua_State* L) { - return detail::static_trampoline<&index_call_with_bases_>(L); - } - - static inline int new_index_call(lua_State* L) { - return detail::static_trampoline<&new_index_call_>(L); - } - - template - static inline int new_index_call_with_bases(lua_State* L) { - return detail::static_trampoline<&new_index_call_with_bases_>(L); + return detail::static_trampoline<&index_call_with_bases_>(L); } + template static inline int meta_index_call(lua_State* L) { - return detail::static_trampoline<&index_call_>(L); + return detail::static_trampoline<&index_call_>(L); } - template + template static inline int meta_index_call_with_bases(lua_State* L) { - return detail::static_trampoline<&index_call_with_bases_>(L); - } - - static inline int meta_new_index_call(lua_State* L) { - return detail::static_trampoline<&new_index_call_>(L); - } - - template - static inline int meta_new_index_call_with_bases(lua_State* L) { - return detail::static_trampoline<&new_index_call_with_bases_>(L); + return detail::static_trampoline<&index_call_with_bases_>(L); } template @@ -557,9 +596,7 @@ namespace sol { namespace u_detail { auto string_it = this->string_keys.find(s); if (string_it != this->string_keys.cend()) { const auto& binding_data = string_it->second.binding_data; - storage_it = std::find_if(this->storage.begin(), this->storage.end(), [&binding_data](const std::unique_ptr& b) { - return b->data() == binding_data; - }); + storage_it = std::find_if(this->storage.begin(), this->storage.end(), binding_data_equals(binding_data)); this->string_keys.erase(string_it); } @@ -580,75 +617,58 @@ namespace sol { namespace u_detail { index_call_storage ics; ics.binding_data = b.data(); ics.index = is_index ? &Binding::template call_with_ : &Binding::template index_call_with_; - ics.new_index = is_new_index ? &Binding::template call_with_ - : &Binding::template index_call_with_; - // need to swap everything to use fast indexing here - auto for_each_backing_metatable = [&](lua_State* L, submetatable_type smt, reference& fast_index_table) { - if (smt == submetatable_type::named) { - // do not override __call or - // other specific meta functions on named metatable: - // we need that for call construction - // and other amenities - return; - } - int fast_index_table_push = fast_index_table.push(); - stack_reference t(L, -fast_index_table_push); - if (poison_indexing) { - change_indexing(L, - smt, - derived_this, - t, - &usertype_storage::index_call, - &usertype_storage::new_index_call, - &usertype_storage::meta_index_call, - &usertype_storage::meta_new_index_call); - } - if (is_destruction - && (smt == submetatable_type::reference || smt == submetatable_type::const_reference || smt == submetatable_type::named - || smt == submetatable_type::unique)) { - // gc does not apply to us here - // for reference types (raw T*, std::ref) - // for the named metatable itself, - // or for unique_usertypes, which do their own custom destruction - t.pop(); - return; - } - if constexpr (is_lua_c_function_v || is_lua_reference_or_proxy::value) { - stack::set_field(L, s, b.data_, t.stack_index()); - } - else { - stack::set_field(L, s, make_closure(&b.template call, nullptr, ics.binding_data), t.stack_index()); - } - t.pop(); - }; + ics.new_index + = is_new_index ? &Binding::template call_with_ : &Binding::template index_call_with_; + + string_for_each_metatable_func for_each_fx; + for_each_fx.is_destruction = is_destruction; + for_each_fx.is_index = is_index; + for_each_fx.is_new_index = is_new_index; + for_each_fx.poison_indexing = poison_indexing; + for_each_fx.p_key = &s; + for_each_fx.p_ics = &ics; + if constexpr (is_lua_c_function_v) { + for_each_fx.is_unqualified_lua_CFunction = true; + for_each_fx.call_func = *static_cast(ics.binding_data); + } + else if constexpr (is_lua_reference_or_proxy_v) { + for_each_fx.is_unqualified_lua_reference = true; + for_each_fx.p_binding_ref = static_cast(ics.binding_data); + } + else { + for_each_fx.call_func = &b.template call; + } + for_each_fx.p_usb = this; + for_each_fx.p_derived_usb = derived_this; + for_each_fx.idx_call = &usertype_storage::template index_call; + for_each_fx.new_idx_call = &usertype_storage::template index_call; + for_each_fx.meta_idx_call = &usertype_storage::template meta_index_call; + for_each_fx.meta_new_idx_call = &usertype_storage::template meta_index_call; + for_each_fx.change_indexing = &usertype_storage_base::change_indexing; + // set base index and base new_index + // functions here if (is_index) { - this->base_index = ics; + this->base_index.index = ics.index; + this->base_index.binding_data = ics.binding_data; } if (is_new_index) { - this->base_new_index = ics; + this->base_index.new_index = ics.new_index; + this->base_index.new_binding_data = ics.binding_data; } - this->for_each_table(L, for_each_backing_metatable); + this->for_each_table(L, for_each_fx); this->add_entry(s, std::move(ics)); } else { // the reference-based implementation might compare poorly and hash // poorly in some cases... - if constexpr (is_lua_reference::value && is_lua_reference::value) { + if constexpr (is_lua_reference_v && is_lua_reference_v) { if (key.get_type() == type::string) { stack::push(L, key); std::string string_key = stack::pop(L); this->set(L, string_key, std::forward(value)); } else { - auto ref_additions_fx = [&](lua_State* L, submetatable_type smt, reference& fast_index_table) { - if (smt == submetatable_type::named) { - return; - } - int fast_index_table_push = fast_index_table.push(); - stack_reference t(L, -fast_index_table_push); - stack::set_field(L, key, value, t.stack_index()); - t.pop(); - }; + lua_reference_func ref_additions_fx{ key, value }; this->for_each_table(L, ref_additions_fx); this->auxiliary_keys.insert_or_assign(std::forward(key), std::forward(value)); @@ -657,15 +677,8 @@ namespace sol { namespace u_detail { else { reference ref_key = make_reference(L, std::forward(key)); reference ref_value = make_reference(L, std::forward(value)); - auto ref_additions_fx = [&](lua_State* L, submetatable_type smt, reference& fast_index_table) { - if (smt == submetatable_type::named) { - return; - } - int fast_index_table_push = fast_index_table.push(); - stack_reference t(L, -fast_index_table_push); - stack::set_field(L, ref_key, ref_value, t.stack_index()); - t.pop(); - }; + lua_reference_func ref_additions_fx{ key, value }; + this->for_each_table(L, ref_additions_fx); this->auxiliary_keys.insert_or_assign(std::move(ref_key), std::move(ref_value)); } @@ -677,7 +690,7 @@ namespace sol { namespace u_detail { void usertype_storage::set(lua_State* L, Key&& key, Value&& value) { static_cast(*this).set(L, std::forward(key), std::forward(value)); } - + template inline usertype_storage& create_usertype_storage(lua_State* L) { const char* gcmetakey = &usertype_traits::gc_table()[0]; @@ -952,11 +965,11 @@ namespace sol { namespace u_detail { stack_reference stack_metametatable(L, -storage.named_index_table.push()); stack::set_field(L, meta_function::index, - make_closure(uts::meta_index_call, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic), + make_closure(uts::template meta_index_call, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic), stack_metametatable.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(uts::meta_new_index_call, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic), + make_closure(uts::template meta_index_call, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic), stack_metametatable.stack_index()); stack_metametatable.pop(); } @@ -966,7 +979,7 @@ namespace sol { namespace u_detail { stack::set_field(L, meta_function::index, t, t.stack_index()); stack::set_field(L, meta_function::new_index, - make_closure(uts::new_index_call, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic), + make_closure(uts::template index_call, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic), t.stack_index()); storage.is_using_new_index = true; } diff --git a/tests/runtime_tests/source/usertype_runtime.cpp b/tests/runtime_tests/source/usertype_runtime.cpp index 3463d237..9e70246e 100644 --- a/tests/runtime_tests/source/usertype_runtime.cpp +++ b/tests/runtime_tests/source/usertype_runtime.cpp @@ -31,6 +31,29 @@ #include #include +struct new_index_test_object { + bool new_indexed = false; + bool borf_new_indexed = false; + + float GetLevel(int x) const { + return static_cast(x); + } + + static void nidx(new_index_test_object& self, std::string_view key, sol::stack_object value) { + if (key == "borf" && value.is() && value.as() == 2) { + self.borf_new_indexed = true; + return; + } + self.new_indexed = true; + } + + static sol::object idx(sol::stack_object self, std::string_view key) { + if (key == "bark") { + return sol::object(self.lua_state(), sol::in_place, &new_index_test_object::GetLevel); + } + return sol::lua_nil; + } +}; TEST_CASE("usertype/runtime-extensibility", "Check if usertypes are runtime extensible") { struct thing { @@ -339,3 +362,44 @@ TEST_CASE("usertype/meta-key-retrievals", "allow for special meta keys (__index, REQUIRE(keys[3] == "__call"); } } + +TEST_CASE("usertype/new_index and index", "a custom new_index and index only kicks in after the values pre-ordained on the index and new_index tables are assigned") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("new_index_test_object", + sol::meta_function::index, + &new_index_test_object::idx, + sol::meta_function::new_index, + &new_index_test_object::nidx, + "Level", + &new_index_test_object::GetLevel); + + const auto& code = R"(a = new_index_test_object.new() + print(a:Level(1)) + print(a:Level(2)) + print(a:Level(3)))"; + + auto result0 = lua.safe_script(code, sol::script_pass_on_error); + REQUIRE(result0.valid()); + auto result1 = lua.safe_script("print(a:bark(1))", sol::script_pass_on_error); + REQUIRE(result1.valid()); + auto result2 = lua.safe_script("assert(a.Level2 == nil)", sol::script_pass_on_error); + REQUIRE(result2.valid()); + auto result3 = lua.safe_script("a:Level3()", sol::script_pass_on_error); + REQUIRE_FALSE(result3.valid()); + + new_index_test_object& a = lua["a"]; + REQUIRE_FALSE(a.borf_new_indexed); + REQUIRE_FALSE(a.new_indexed); + + auto resultnormal = lua.safe_script("a.normal = 'foo'", sol::script_pass_on_error); + REQUIRE(resultnormal.valid()); + REQUIRE_FALSE(a.borf_new_indexed); + REQUIRE(a.new_indexed); + + auto resultborf = lua.safe_script("a.borf = 2", sol::script_pass_on_error); + REQUIRE(resultborf.valid()); + REQUIRE(a.borf_new_indexed); + REQUIRE(a.new_indexed); +}