Add move semantics for non-raii hpp handles. (#1919)

This commit is contained in:
Nikolai Siukosev 2024-07-18 16:25:05 +04:00 committed by Nikolai Siukosev
parent f7f0d8ccaa
commit f35e9c9f9b
7 changed files with 112 additions and 20 deletions

View File

@ -892,6 +892,10 @@ When this is not externally defined and `VULKAN_HPP_CPP_VERSION` is at least `23
By default, the member `m_mask` of the `Flags` class template is private. This is to prevent accidentally setting a `Flags` with some inappropriate value. But it also prevents using a `Flags`, or a structure holding a `Flags`, to be used as a non-type template parameter. If you really need that functionality, and accept the reduced security, you can use this define to change the access specifier for `m_mask` from private to public, which allows using a `Flags` as a non-type template parameter. By default, the member `m_mask` of the `Flags` class template is private. This is to prevent accidentally setting a `Flags` with some inappropriate value. But it also prevents using a `Flags`, or a structure holding a `Flags`, to be used as a non-type template parameter. If you really need that functionality, and accept the reduced security, you can use this define to change the access specifier for `m_mask` from private to public, which allows using a `Flags` as a non-type template parameter.
#### VULKAN_HPP_HANDLES_MOVE_EXCHANGE
This define can be used to enable `m_handle = exchange( rhs.m_handle, {} )` in move constructors of Vulkan-Hpp handles, which default-initializes the `rhs` underlying value. By default Vulkan-Hpp handles behave like trivial types -- move constructors copying value.
#### VULKAN_HPP_HASH_COMBINE #### VULKAN_HPP_HASH_COMBINE
This define can be used to specify your own hash combiner function. In order to determine the hash of a vk-structure, the hashes of the members of that struct are to be combined. This is done by this define, which by default is identical to what the function `boost::hash_combine()` does. It gets the type of the to-be-combined value, the seed, which is the combined value up to that point, and finally the to-be-combined value. This hash calculation determines a "shallow" hash, as it takes the hashes of any pointer in a structure, and not the hash of a pointed-to value. This define can be used to specify your own hash combiner function. In order to determine the hash of a vk-structure, the hashes of the members of that struct are to be combined. This is done by this define, which by default is identical to what the function `boost::hash_combine()` does. It gets the type of the to-be-combined value, the seed, which is the combined value up to that point, and finally the to-be-combined value. This hash calculation determines a "shallow" hash, as it takes the hashes of any pointer in a structure, and not the hash of a pointed-to value.

View File

@ -458,6 +458,7 @@ ${UniqueHandle}
${DispatchLoaderBase} ${DispatchLoaderBase}
${DispatchLoaderStatic} ${DispatchLoaderStatic}
${Exchange}
#if !defined( VULKAN_HPP_NO_SMART_HANDLE ) #if !defined( VULKAN_HPP_NO_SMART_HANDLE )
${ObjectDestroy} ${ObjectDestroy}
${ObjectFree} ${ObjectFree}
@ -532,6 +533,7 @@ ${DispatchLoaderDynamic}
{ "DispatchLoaderStatic", generateDispatchLoaderStatic() }, { "DispatchLoaderStatic", generateDispatchLoaderStatic() },
{ "DynamicLoader", readSnippet( "DynamicLoader.hpp" ) }, { "DynamicLoader", readSnippet( "DynamicLoader.hpp" ) },
{ "Exceptions", readSnippet( "Exceptions.hpp" ) }, { "Exceptions", readSnippet( "Exceptions.hpp" ) },
{ "Exchange", readSnippet( "Exchange.hpp" ) },
{ "headerVersion", m_version }, { "headerVersion", m_version },
{ "includes", replaceWithMap( readSnippet( "includes.hpp" ), { { "vulkan_h", ( m_api == "vulkan" ) ? "vulkan.h" : "vulkan_sc_core.h" } } ) }, { "includes", replaceWithMap( readSnippet( "includes.hpp" ), { { "vulkan_h", ( m_api == "vulkan" ) ? "vulkan.h" : "vulkan_sc_core.h" } } ) },
{ "licenseHeader", m_vulkanLicenseHeader }, { "licenseHeader", m_vulkanLicenseHeader },
@ -582,7 +584,7 @@ void VulkanHppGenerator::generateRAIIHppFile() const
#define VULKAN_RAII_HPP #define VULKAN_RAII_HPP
#include <memory> // std::unique_ptr #include <memory> // std::unique_ptr
#include <utility> // std::exchange, std::forward #include <utility> // std::forward
#include <vulkan/${api}.hpp> #include <vulkan/${api}.hpp>
#if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE ) #if !defined( VULKAN_HPP_DISABLE_ENHANCED_MODE )
@ -590,18 +592,6 @@ namespace VULKAN_HPP_NAMESPACE
{ {
namespace VULKAN_HPP_RAII_NAMESPACE namespace VULKAN_HPP_RAII_NAMESPACE
{ {
# if ( 14 <= VULKAN_HPP_CPP_VERSION )
using std::exchange;
# else
template <class T, class U = T>
VULKAN_HPP_CONSTEXPR_14 VULKAN_HPP_INLINE T exchange( T & obj, U && newValue )
{
T oldValue = std::move( obj );
obj = std::forward<U>( newValue );
return oldValue;
}
# endif
template <class T> template <class T>
class CreateReturnType class CreateReturnType
{ {
@ -5751,6 +5741,7 @@ std::string VulkanHppGenerator::generateCppModuleUsings() const
auto const hardCodedEnhancedModeTypes = std::array{ "ArrayProxy", "ArrayProxyNoTemporaries", "StridedArrayProxy", "Optional", "StructureChain" }; auto const hardCodedEnhancedModeTypes = std::array{ "ArrayProxy", "ArrayProxyNoTemporaries", "StridedArrayProxy", "Optional", "StructureChain" };
auto const hardCodedSmartHandleTypes = std::array{ "ObjectDestroy", "ObjectFree", "ObjectRelease", "PoolFree", "ObjectDestroyShared", auto const hardCodedSmartHandleTypes = std::array{ "ObjectDestroy", "ObjectFree", "ObjectRelease", "PoolFree", "ObjectDestroyShared",
"ObjectFreeShared", "ObjectReleaseShared", "PoolFreeShared", "SharedHandle", "UniqueHandle" }; "ObjectFreeShared", "ObjectReleaseShared", "PoolFreeShared", "SharedHandle", "UniqueHandle" };
auto const hardCodedFunctions = std::array{ "exchange" };
auto usings = std::string{ R"( //===================================== auto usings = std::string{ R"( //=====================================
//=== HARDCODED TYPEs AND FUNCTIONs === //=== HARDCODED TYPEs AND FUNCTIONs ===
@ -5794,6 +5785,11 @@ std::string VulkanHppGenerator::generateCppModuleUsings() const
const auto [enterNoSmartHandle, leaveNoSmartHandle] = generateProtection( "VULKAN_HPP_NO_SMART_HANDLE", false ); const auto [enterNoSmartHandle, leaveNoSmartHandle] = generateProtection( "VULKAN_HPP_NO_SMART_HANDLE", false );
usings += "\n" + enterNoSmartHandle + noSmartHandleUsings + leaveNoSmartHandle + "\n"; usings += "\n" + enterNoSmartHandle + noSmartHandleUsings + leaveNoSmartHandle + "\n";
for ( auto const& functionName : hardCodedFunctions )
{
usings += "\n" + replaceWithMap( usingTemplate, { { "className", std::string{ functionName } } } );
}
// now generate baseTypes // now generate baseTypes
auto baseTypes = std::string{ R"( auto baseTypes = std::string{ R"(
//================== //==================
@ -5906,7 +5902,6 @@ std::string VulkanHppGenerator::generateCppModuleRaiiUsings() const
//=== RAII HARDCODED === //=== RAII HARDCODED ===
//====================== //======================
using VULKAN_HPP_RAII_NAMESPACE::exchange;
using VULKAN_HPP_RAII_NAMESPACE::Context; using VULKAN_HPP_RAII_NAMESPACE::Context;
using VULKAN_HPP_RAII_NAMESPACE::ContextDispatcher; using VULKAN_HPP_RAII_NAMESPACE::ContextDispatcher;
using VULKAN_HPP_RAII_NAMESPACE::InstanceDispatcher; using VULKAN_HPP_RAII_NAMESPACE::InstanceDispatcher;
@ -7823,8 +7818,20 @@ ${enter} class ${className}
${className}() VULKAN_HPP_NOEXCEPT {}; // = default - try to workaround a compiler issue ${className}() VULKAN_HPP_NOEXCEPT {}; // = default - try to workaround a compiler issue
${className}( ${className} const & rhs ) = default; ${className}( ${className} const & rhs ) = default;
${className} & operator=( ${className} const & rhs ) = default; ${className} & operator=( ${className} const & rhs ) = default;
#if !defined(VULKAN_HPP_HANDLES_MOVE_EXCHANGE)
${className}( ${className} && rhs ) = default; ${className}( ${className} && rhs ) = default;
${className} & operator=( ${className} && rhs ) = default; ${className} & operator=( ${className} && rhs ) = default;
#else
${className}( ${className} && rhs ) VULKAN_HPP_NOEXCEPT
: m_${memberName}( VULKAN_HPP_NAMESPACE::exchange( rhs.m_${memberName}, {} ) )
{}
${className} & operator=( ${className} && rhs ) VULKAN_HPP_NOEXCEPT
{
m_${memberName} = VULKAN_HPP_NAMESPACE::exchange( rhs.m_${memberName}, {} );
return *this;
}
#endif
VULKAN_HPP_CONSTEXPR ${className}( std::nullptr_t ) VULKAN_HPP_NOEXCEPT VULKAN_HPP_CONSTEXPR ${className}( std::nullptr_t ) VULKAN_HPP_NOEXCEPT
{} {}
@ -9939,7 +9946,7 @@ std::tuple<std::string, std::string, std::string, std::string, std::string, std:
if ( !memberName.empty() ) if ( !memberName.empty() )
{ {
clearMembers += "\n m_" + memberName + " = nullptr;"; clearMembers += "\n m_" + memberName + " = nullptr;";
moveConstructorInitializerList += "m_" + memberName + "( VULKAN_HPP_NAMESPACE::VULKAN_HPP_RAII_NAMESPACE::exchange( rhs.m_" + memberName + ", {} ) ), "; moveConstructorInitializerList += "m_" + memberName + "( VULKAN_HPP_NAMESPACE::exchange( rhs.m_" + memberName + ", {} ) ), ";
moveAssignmentInstructions += "\n std::swap( m_" + memberName + ", rhs.m_" + memberName + " );"; moveAssignmentInstructions += "\n std::swap( m_" + memberName + ", rhs.m_" + memberName + " );";
memberVariables += "\n " + memberType + " m_" + memberName + " = {};"; memberVariables += "\n " + memberType + " m_" + memberName + " = {};";
swapMembers += "\n std::swap( m_" + memberName + ", rhs.m_" + memberName + " );"; swapMembers += "\n std::swap( m_" + memberName + ", rhs.m_" + memberName + " );";
@ -9966,14 +9973,14 @@ std::tuple<std::string, std::string, std::string, std::string, std::string, std:
std::string frontName = handle.second.constructorIts.front()->second.params.front().name; std::string frontName = handle.second.constructorIts.front()->second.params.front().name;
clearMembers += "\n m_" + frontName + " = nullptr;"; clearMembers += "\n m_" + frontName + " = nullptr;";
moveConstructorInitializerList = "m_" + frontName + "( VULKAN_HPP_NAMESPACE::VULKAN_HPP_RAII_NAMESPACE::exchange( rhs.m_" + frontName + ", {} ) ), "; moveConstructorInitializerList = "m_" + frontName + "( VULKAN_HPP_NAMESPACE::exchange( rhs.m_" + frontName + ", {} ) ), ";
moveAssignmentInstructions = "\n std::swap( m_" + frontName + ", rhs.m_" + frontName + " );"; moveAssignmentInstructions = "\n std::swap( m_" + frontName + ", rhs.m_" + frontName + " );";
memberVariables = "\n VULKAN_HPP_NAMESPACE::" + stripPrefix( frontType, "Vk" ) + " m_" + frontName + " = {};"; memberVariables = "\n VULKAN_HPP_NAMESPACE::" + stripPrefix( frontType, "Vk" ) + " m_" + frontName + " = {};";
swapMembers = "\n std::swap( m_" + frontName + ", rhs.m_" + frontName + " );"; swapMembers = "\n std::swap( m_" + frontName + ", rhs.m_" + frontName + " );";
releaseMembers += "\n m_" + frontName + " = nullptr;"; releaseMembers += "\n m_" + frontName + " = nullptr;";
} }
clearMembers += "\n m_" + handleName + " = nullptr;"; clearMembers += "\n m_" + handleName + " = nullptr;";
moveConstructorInitializerList += "m_" + handleName + "( VULKAN_HPP_NAMESPACE::VULKAN_HPP_RAII_NAMESPACE::exchange( rhs.m_" + handleName + ", {} ) ), "; moveConstructorInitializerList += "m_" + handleName + "( VULKAN_HPP_NAMESPACE::exchange( rhs.m_" + handleName + ", {} ) ), ";
moveAssignmentInstructions += "\n std::swap( m_" + handleName + ", rhs.m_" + handleName + " );"; moveAssignmentInstructions += "\n std::swap( m_" + handleName + ", rhs.m_" + handleName + " );";
memberVariables += "\n " + generateNamespacedType( handle.first ) + " m_" + handleName + " = {};"; memberVariables += "\n " + generateNamespacedType( handle.first ) + " m_" + handleName + " = {};";
swapMembers += "\n std::swap( m_" + handleName + ", rhs.m_" + handleName + " );"; swapMembers += "\n std::swap( m_" + handleName + ", rhs.m_" + handleName + " );";
@ -9985,7 +9992,7 @@ std::tuple<std::string, std::string, std::string, std::string, std::string, std:
memberVariables += "\n VULKAN_HPP_NAMESPACE::Result m_constructorSuccessCode = VULKAN_HPP_NAMESPACE::Result::eErrorUnknown;"; memberVariables += "\n VULKAN_HPP_NAMESPACE::Result m_constructorSuccessCode = VULKAN_HPP_NAMESPACE::Result::eErrorUnknown;";
swapMembers += "\n std::swap( m_constructorSuccessCode, rhs.m_constructorSuccessCode );"; swapMembers += "\n std::swap( m_constructorSuccessCode, rhs.m_constructorSuccessCode );";
moveConstructorInitializerList += moveConstructorInitializerList +=
"m_constructorSuccessCode( VULKAN_HPP_NAMESPACE::VULKAN_HPP_RAII_NAMESPACE::exchange( rhs.m_constructorSuccessCode, {} ) ), "; "m_constructorSuccessCode( VULKAN_HPP_NAMESPACE::exchange( rhs.m_constructorSuccessCode, {} ) ), ";
moveAssignmentInstructions += "\n std::swap( m_constructorSuccessCode, rhs.m_constructorSuccessCode );"; moveAssignmentInstructions += "\n std::swap( m_constructorSuccessCode, rhs.m_constructorSuccessCode );";
releaseMembers += "\n m_constructorSuccessCode = VULKAN_HPP_NAMESPACE::Result::eErrorUnknown;"; releaseMembers += "\n m_constructorSuccessCode = VULKAN_HPP_NAMESPACE::Result::eErrorUnknown;";
} }
@ -10009,7 +10016,7 @@ std::tuple<std::string, std::string, std::string, std::string, std::string, std:
clearMembers += "\n m_dispatcher = nullptr;"; clearMembers += "\n m_dispatcher = nullptr;";
swapMembers += "\n std::swap( m_dispatcher, rhs.m_dispatcher );"; swapMembers += "\n std::swap( m_dispatcher, rhs.m_dispatcher );";
releaseMembers += "\n m_dispatcher = nullptr;"; releaseMembers += "\n m_dispatcher = nullptr;";
releaseMembers += "\n return VULKAN_HPP_NAMESPACE::VULKAN_HPP_RAII_NAMESPACE::exchange( m_" + handleName + ", nullptr );"; releaseMembers += "\n return VULKAN_HPP_NAMESPACE::exchange( m_" + handleName + ", nullptr );";
if ( ( handle.first == "VkInstance" ) || ( handle.first == "VkDevice" ) ) if ( ( handle.first == "VkInstance" ) || ( handle.first == "VkDevice" ) )
{ {
@ -10017,7 +10024,7 @@ std::tuple<std::string, std::string, std::string, std::string, std::string, std:
} }
else else
{ {
moveConstructorInitializerList += "m_dispatcher( VULKAN_HPP_NAMESPACE::VULKAN_HPP_RAII_NAMESPACE::exchange( rhs.m_dispatcher, nullptr ) )"; moveConstructorInitializerList += "m_dispatcher( VULKAN_HPP_NAMESPACE::exchange( rhs.m_dispatcher, nullptr ) )";
} }
moveAssignmentInstructions += "\n std::swap( m_dispatcher, rhs.m_dispatcher );"; moveAssignmentInstructions += "\n std::swap( m_dispatcher, rhs.m_dispatcher );";

11
snippets/Exchange.hpp Normal file
View File

@ -0,0 +1,11 @@
#if ( 14 <= VULKAN_HPP_CPP_VERSION )
using std::exchange;
#else
template <class T, class U = T>
VULKAN_HPP_CONSTEXPR_14 VULKAN_HPP_INLINE T exchange( T & obj, U && newValue )
{
T oldValue = std::move( obj );
obj = std::forward<U>( newValue );
return oldValue;
}
#endif

View File

@ -2,6 +2,7 @@
#include <array> // ArrayWrapperND #include <array> // ArrayWrapperND
#include <string.h> // strnlen #include <string.h> // strnlen
#include <string> // std::string #include <string> // std::string
#include <utility> // std::exchange
#include <vulkan/${vulkan_h}> #include <vulkan/${vulkan_h}>
#include <vulkan/vulkan_hpp_macros.hpp> #include <vulkan/vulkan_hpp_macros.hpp>

View File

@ -30,6 +30,7 @@ add_subdirectory( ExtensionInspection )
add_subdirectory( Flags ) add_subdirectory( Flags )
add_subdirectory( FormatTraits ) add_subdirectory( FormatTraits )
add_subdirectory( Handles ) add_subdirectory( Handles )
add_subdirectory( HandlesMoveExchange )
add_subdirectory( Hash ) add_subdirectory( Hash )
add_subdirectory( NoExceptions ) add_subdirectory( NoExceptions )
if( ( "cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES ) AND NOT ( ( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) AND ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0 ) ) ) if( ( "cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES ) AND NOT ( ( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) AND ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0 ) ) )

View File

@ -0,0 +1,19 @@
# Copyright(c) 2024, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if( NOT VULKAN_HPP_TESTS_BUILD_ONLY_DYNAMIC )
find_package( Vulkan REQUIRED )
vulkan_hpp__setup_test( NAME HandlesMoveExchange LIBRARIES ${Vulkan_LIBRARIES} )
endif()

View File

@ -0,0 +1,49 @@
// Copyright(c) 2024, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#undef VULKAN_HPP_DISPATCH_LOADER_DYNAMIC
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 0
#define VULKAN_HPP_HANDLES_MOVE_EXCHANGE
#include <iostream>
#include <vulkan/vulkan.hpp>
int main( int /*argc*/, char ** /*argv*/ )
{
try
{
vk::Instance instance;
instance = vk::createInstance( {} );
assert( instance != nullptr );
vk::Instance anotherInstance = std::move( instance );
assert( instance == nullptr );
assert( anotherInstance != nullptr );
anotherInstance.destroy();
}
catch ( vk::SystemError const & err )
{
std::cout << "vk::SystemError: " << err.what() << std::endl;
exit( -1 );
}
catch ( ... )
{
std::cout << "unknown error\n";
exit( -1 );
}
return 0;
}