From ee299a45f0f1f1ef234ecf8d468c74cd0c4c20b8 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 11 May 2013 10:05:08 +0200 Subject: [PATCH] Work on error handling. I replaced some asserts with exceptions and also added basic error handling documentation. --- documentation/ErrorHandling.rst | 10 +++++ documentation/index.rst | 1 + .../include/PolyVoxCore/AStarPathfinder.inl | 2 +- .../AmbientOcclusionCalculator.inl | 19 +++++--- .../PolyVoxCore/include/PolyVoxCore/Array.inl | 43 ++++++++++++++----- .../include/PolyVoxCore/BaseVolume.inl | 22 +++++----- .../include/PolyVoxCore/BaseVolumeSampler.inl | 2 +- .../include/PolyVoxCore/ConstVolumeProxy.h | 4 ++ .../PolyVoxCore/CubicSurfaceExtractor.inl | 6 +-- .../include/PolyVoxCore/Impl/Block.inl | 31 +++++++++---- .../include/PolyVoxCore/Impl/ErrorHandling.h | 33 +++++++++++++- .../include/PolyVoxCore/Impl/SubArray.inl | 24 +++++++++-- .../include/PolyVoxCore/Interpolation.h | 3 ++ .../include/PolyVoxCore/LargeVolume.inl | 17 +++----- .../PolyVoxCore/LargeVolumeSampler.inl | 2 +- .../include/PolyVoxCore/LowPassFilter.inl | 11 ++--- .../include/PolyVoxCore/RawVolume.inl | 23 +++++++--- .../include/PolyVoxCore/SimpleVolume.inl | 15 +++---- .../include/PolyVoxCore/SimpleVolumeBlock.inl | 7 +-- .../include/PolyVoxCore/Vector.inl | 19 +++++++- library/PolyVoxCore/source/Impl/Utility.cpp | 4 -- library/PolyVoxCore/source/Region.cpp | 5 ++- 22 files changed, 213 insertions(+), 90 deletions(-) create mode 100644 documentation/ErrorHandling.rst diff --git a/documentation/ErrorHandling.rst b/documentation/ErrorHandling.rst new file mode 100644 index 00000000..75ced791 --- /dev/null +++ b/documentation/ErrorHandling.rst @@ -0,0 +1,10 @@ +************** +Error Handling +************** +Error handling in PolyVox is provided by using the C++ exception mechanism. Exceptions can be thrown for a variety of reasons and your code should be prepared to handle them to prevent your application from crashing. + +Most functions in PolyVox will validate their input parameters and throw an exception if the provided values do not meet the function's requirements (which should be specified in the API documentation). However, in certain performance critical cases we choose not to spend time validating the parameters and an exception will not be thrown, though we do still use an assertion if these are enabled. + +The most notable example of this is when accessing volume data through the get/setVoxel() functions, as these are designed to be very fast. Validating an input position would require multiple conditional operations which we chose to avoid. Therefore, **accessing a voxel outside of a volume will cause undefined behaviour.** When reading voxels it is safer to use the function getVoxelWithWrapping() as this lets you specify how out-of-bounds voxels should be handled. + +In addition to the C++ exception handling mechanism, PolyVox also makes use of assertions to verify the internal state of the library at various points. If you hit an assert in PolyVox then there is a good chance it is a bug in the library, as user errors should have been prevented by throwing an exceptions. \ No newline at end of file diff --git a/documentation/index.rst b/documentation/index.rst index 0cec613c..8f5ac27b 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -14,6 +14,7 @@ User Guide: ModifyingTerrain LevelOfDetail Threading + ErrorHandling Examples: diff --git a/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl b/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl index cf490afe..f9a59a1b 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AStarPathfinder.inl @@ -298,7 +298,7 @@ namespace PolyVox hVal = SixConnectedCost(a, b); break; default: - POLYVOX_ASSERT(false, "Invalid case"); + POLYVOX_THROW(std::invalid_argument, "Connectivity parameter has an unrecognised value."); } //Sanity checks in debug mode. These can come out eventually, but I diff --git a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl index 27ff3188..82c266f3 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/AmbientOcclusionCalculator.inl @@ -34,15 +34,24 @@ namespace PolyVox template void calculateAmbientOcclusion(VolumeType* volInput, Array<3, uint8_t>* arrayResult, Region region, float fRayLength, uint8_t uNoOfSamplesPerOutputElement, IsVoxelTransparentCallback isVoxelTransparentCallback) { + //Make sure that the size of the volume is an exact multiple of the size of the array. + if(volInput->getWidth() % arrayResult->getDimension(0) != 0) + { + POLYVOX_THROW(std::invalid_argument, "Volume width must be an exact multiple of array width."); + } + if(volInput->getHeight() % arrayResult->getDimension(1) != 0) + { + POLYVOX_THROW(std::invalid_argument, "Volume width must be an exact multiple of array height."); + } + if(volInput->getDepth() % arrayResult->getDimension(2) != 0) + { + POLYVOX_THROW(std::invalid_argument, "Volume width must be an exact multiple of array depth."); + } + uint16_t uRandomUnitVectorIndex = 0; uint16_t uRandomVectorIndex = 0; uint16_t uIndexIncreament; - //Make sure that the size of the volume is an exact multiple of the size of the array. - POLYVOX_ASSERT(volInput->getWidth() % arrayResult->getDimension(0) == 0, "Volume width must be an exact multiple of array width."); - POLYVOX_ASSERT(volInput->getHeight() % arrayResult->getDimension(1) == 0, "Volume height must be an exact multiple of array height."); - POLYVOX_ASSERT(volInput->getDepth() % arrayResult->getDimension(2) == 0, "Volume depth must be an exact multiple of array depth."); - //Our initial indices. It doesn't matter exactly what we set here, but the code below makes //sure they are different for different regions which helps reduce tiling patterns in the results. uRandomUnitVectorIndex += region.getLowerX() + region.getLowerY() + region.getLowerZ(); diff --git a/library/PolyVoxCore/include/PolyVoxCore/Array.inl b/library/PolyVoxCore/include/PolyVoxCore/Array.inl index 366180c7..1821817d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Array.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Array.inl @@ -73,7 +73,11 @@ namespace PolyVox template SubArray Array::operator[](uint32_t uIndex) { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return SubArray(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -91,7 +95,11 @@ namespace PolyVox template const SubArray Array::operator[](uint32_t uIndex) const { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return SubArray(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -139,7 +147,10 @@ namespace PolyVox m_uNoOfElements = 1; for (uint32_t i = 0; i uint32_t Array::getDimension(uint32_t uDimension) { - POLYVOX_ASSERT(uDimension < noOfDims, "Dimension out of range"); + if(uDimension >= noOfDims) + { + POLYVOX_THROW(std::out_of_range, "Array dimension out of range"); + } + return m_pDimensions[uDimension]; } @@ -198,14 +213,14 @@ namespace PolyVox ,m_uNoOfElements(0) { //Not implemented - POLYVOX_ASSERT(false, "Not implemented."); + POLYVOX_THROW(not_implemented, "This function is not implemented and should never be called!"); } template Array& Array::operator=(const Array& rhs) { //Not implemented - POLYVOX_ASSERT(false, "Not implemented."); + POLYVOX_THROW(not_implemented, "This function is not implemented and should never be called!"); return *this; } @@ -251,14 +266,22 @@ namespace PolyVox template ElementType& Array<1, ElementType>::operator[] (uint32_t uIndex) { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return m_pElements[uIndex]; } template const ElementType& Array<1, ElementType>::operator[] (uint32_t uIndex) const { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return m_pElements[uIndex]; } @@ -307,14 +330,14 @@ namespace PolyVox ,m_pDimensions(0) { //Not implemented - POLYVOX_ASSERT(false, "Not implemented."); + POLYVOX_THROW(not_implemented, "This function is not implemented and should never be called!"); } template Array<1, ElementType>& Array<1, ElementType>::operator=(const Array<1, ElementType>& rhs) { //Not implemented - POLYVOX_ASSERT(false, "Not implemented."); + POLYVOX_THROW(not_implemented, "This function is not implemented and should never be called!"); return *this; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl index 4a525440..2103726d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolume.inl @@ -45,7 +45,7 @@ namespace PolyVox template BaseVolume::BaseVolume(const BaseVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -59,14 +59,14 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// /// This function should never be called. Copying volumes by value would be expensive, and we want to prevent users from doing /// it by accident (such as when passing them as paramenters to functions). That said, there are times when you really do want to - /// make a copy of a volume and in this case you should look at the Volumeresampler. + /// make a copy of a volume and in this case you should look at the VolumeResampler. /// /// \sa VolumeResampler //////////////////////////////////////////////////////////////////////////////// template BaseVolume& BaseVolume::operator=(const BaseVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -162,7 +162,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxel(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/) const { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return VoxelType(); } @@ -173,7 +173,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxel(const Vector3DInt32& /*v3dPos*/) const { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return VoxelType(); } @@ -186,7 +186,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelAt(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/) const { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return VoxelType(); } @@ -197,7 +197,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelAt(const Vector3DInt32& /*v3dPos*/) const { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return VoxelType(); } @@ -210,7 +210,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelWithWrapping(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/, WrapMode /*eWrapMode*/, VoxelType /*tBorder*/) const { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return VoxelType(); } @@ -221,7 +221,7 @@ namespace PolyVox template VoxelType BaseVolume::getVoxelWithWrapping(const Vector3DInt32& /*v3dPos*/, WrapMode /*eWrapMode*/, VoxelType /*tBorder*/) const { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return VoxelType(); } @@ -244,7 +244,7 @@ namespace PolyVox template bool BaseVolume::setVoxelAt(int32_t /*uXPos*/, int32_t /*uYPos*/, int32_t /*uZPos*/, VoxelType /*tValue*/) { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return false; } @@ -256,7 +256,7 @@ namespace PolyVox template bool BaseVolume::setVoxelAt(const Vector3DInt32& /*v3dPos*/, VoxelType /*tValue*/) { - POLYVOX_ASSERT(false, "You should never call the base class version of this function."); + POLYVOX_THROW(not_implemented, "You should never call the base class version of this function."); return false; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl index a941dd8b..e8300ccf 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/BaseVolumeSampler.inl @@ -375,7 +375,7 @@ namespace PolyVox default: { //Should never happen - POLYVOX_ASSERT(false, "Invalid case."); + POLYVOX_THROW(std::invalid_argument, "Wrap mode parameter has an unrecognised value."); return VoxelType(); } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h b/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h index 63cc16ca..237a1c59 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h +++ b/library/PolyVoxCore/include/PolyVoxCore/ConstVolumeProxy.h @@ -37,24 +37,28 @@ namespace PolyVox public: VoxelType getVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); return m_pVolume.getVoxelAt(uXPos, uYPos, uZPos); } VoxelType getVoxelAt(const Vector3DInt32& v3dPos) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(m_regValid.containsPoint(v3dPos), "Position is outside valid region"); return getVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ()); } void setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(m_regValid.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); m_pVolume.setVoxelAtConst(uXPos, uYPos, uZPos, tValue); } void setVoxelAt(const Vector3DInt32& v3dPos, VoxelType tValue) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(m_regValid.containsPoint(v3dPos), "Position is outside valid region"); setVoxelAt(v3dPos.getX(), v3dPos.getY(), v3dPos.getZ(), tValue); } diff --git a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl index 0b0ed9eb..96692bdb 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/CubicSurfaceExtractor.inl @@ -220,9 +220,9 @@ namespace PolyVox } } - // If we exit the loop here then apparently all the slots were full but none of them matched. I don't think - // this can happen so let's put an assert to make sure. If you hit this assert then please report it to us! - POLYVOX_ASSERT(false, "All slots full but no matches."); + // If we exit the loop here then apparently all the slots were full but none of them matched. + // This shouldn't ever happen, so if it does it is probably a bug in PolyVox. Please report it to us! + POLYVOX_THROW(std::runtime_error, "All slots full but no matches during cubic surface extraction. This is probably a bug in PolyVox"); return -1; //Should never happen. } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl index 34e59898..bb95f584 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/Block.inl @@ -60,10 +60,10 @@ namespace PolyVox template VoxelType Block::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const { + // This is internal code not directly called by the user. For efficiency we assert rather than throwing. POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the block"); POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the block"); POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the block"); - POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data - block must be decompressed before accessing voxels."); return m_tUncompressedData @@ -83,10 +83,10 @@ namespace PolyVox template void Block::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue) { + // This is internal code not directly called by the user. For efficiency we assert rather than throwing. POLYVOX_ASSERT(uXPos < m_uSideLength, "Supplied position is outside of the block"); POLYVOX_ASSERT(uYPos < m_uSideLength, "Supplied position is outside of the block"); POLYVOX_ASSERT(uZPos < m_uSideLength, "Supplied position is outside of the block"); - POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data - block must be decompressed before accessing voxels."); m_tUncompressedData @@ -108,9 +108,6 @@ namespace PolyVox template void Block::initialise(uint16_t uSideLength) { - //Debug mode validation - POLYVOX_ASSERT(isPowerOf2(uSideLength), "Block side length must be a power of two."); - //Release mode validation if(!isPowerOf2(uSideLength)) { @@ -141,8 +138,16 @@ namespace PolyVox template void Block::compress(Compressor* pCompressor) { - POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); - POLYVOX_ASSERT(m_bIsCompressed == false, "Attempted to compress block which is already flagged as compressed."); + if(m_bIsCompressed) + { + POLYVOX_THROW(invalid_operation, "Attempted to compress block which is already flagged as compressed."); + } + + if(!pCompressor) + { + POLYVOX_THROW(std::invalid_argument, "A valid compressor must be provided"); + } + POLYVOX_ASSERT(m_tUncompressedData != 0, "No uncompressed data is present."); //If the uncompressed data hasn't actually been @@ -212,8 +217,16 @@ namespace PolyVox template void Block::uncompress(Compressor* pCompressor) { - POLYVOX_ASSERT(pCompressor, "Compressor is not valid"); - POLYVOX_ASSERT(m_bIsCompressed == true, "Attempted to uncompress block which is not flagged as compressed."); + if(!m_bIsCompressed) + { + POLYVOX_THROW(invalid_operation, "Attempted to uncompress block which is not flagged as compressed."); + } + + if(!pCompressor) + { + POLYVOX_THROW(std::invalid_argument, "A valid compressor must be provided"); + } + POLYVOX_ASSERT(m_tUncompressedData == 0, "Uncompressed data already exists."); m_tUncompressedData = new VoxelType[m_uSideLength * m_uSideLength * m_uSideLength]; diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h b/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h index 4b4ee263..0fb01f31 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/ErrorHandling.h @@ -26,9 +26,10 @@ freely, subject to the following restrictions: #include "PolyVoxCore/Impl/Config.h" -#include //For std::exit -#include //For std::cerr +#include // For std::exit +#include // For std::cerr #include +#include // Exception constuctors take strings. #if defined(_MSC_VER) // In Visual Studio we can use this function to go into the debugger. @@ -148,4 +149,32 @@ freely, subject to the following restrictions: getThrowHandler()((except), __FILE__, __LINE__) #endif +namespace PolyVox +{ + /// A general purpose exception to indicate that an operation cannot be peformed. + class invalid_operation : public std::logic_error + { + public: + explicit invalid_operation(const std::string& message) + : logic_error(message.c_str()) {} + + explicit invalid_operation(const char *message) + : logic_error(message) {} + }; + + /// Thrown to indicate that a function is deliberatly not implmented. For example, perhaps you called a function + /// in a base class whereas you are supposed to use a derived class which implements the function, or perhaps the + /// function is not defined for a particular template parameter. It may be that the function is required to + /// compile sucessfully but it should not be called. + class not_implemented : public std::logic_error + { + public: + explicit not_implemented(const std::string& message) + : logic_error(message.c_str()) {} + + explicit not_implemented(const char *message) + : logic_error(message) {} + }; +} + #endif //__PolyVox_ErrorHandling_H__ diff --git a/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl b/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl index c1285d60..81c526c3 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Impl/SubArray.inl @@ -28,7 +28,11 @@ namespace PolyVox template SubArray SubArray::operator[](uint32_t uIndex) { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return SubArray(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -37,7 +41,11 @@ namespace PolyVox template const SubArray SubArray::operator[](uint32_t uIndex) const { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return SubArray(&m_pElements[uIndex*m_pOffsets[0]], m_pDimensions+1, m_pOffsets+1); @@ -56,14 +64,22 @@ namespace PolyVox template ElementType& SubArray<1, ElementType>::operator[] (uint32_t uIndex) { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return m_pElements[uIndex]; } template const ElementType& SubArray<1, ElementType>::operator[] (uint32_t uIndex) const { - POLYVOX_ASSERT(uIndex < m_pDimensions[0], "Index out of range"); + if(uIndex >= m_pDimensions[0]) + { + POLYVOX_THROW(std::out_of_range, "Array index out of range"); + } + return m_pElements[uIndex]; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h index 4b6f2539..07815546 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h +++ b/library/PolyVoxCore/include/PolyVoxCore/Interpolation.h @@ -31,6 +31,7 @@ namespace PolyVox const Type& v0,const Type& v1, const float x) { + //This function is called frequently and is very short, so assert rather than exceptions. POLYVOX_ASSERT((x >= 0.0f) && (x <= 1.0f), "Interpolation input out of 0.0 to 1.0 range."); //Interpolate along X @@ -44,6 +45,7 @@ namespace PolyVox const Type& v00,const Type& v10,const Type& v01,const Type& v11, const float x, const float y) { + //This function is called frequently and is very short, so assert rather than exceptions. POLYVOX_ASSERT((x >= 0.0f) && (y >= 0.0f) && (x <= 1.0f) && (y <= 1.0f), "Interpolation input out of 0.0 to 1.0 range."); @@ -63,6 +65,7 @@ namespace PolyVox const Type& v001,const Type& v101,const Type& v011,const Type& v111, const float x, const float y, const float z) { + //This function is called frequently and is very short, so assert rather than exceptions. POLYVOX_ASSERT((x >= 0.0f) && (y >= 0.0f) && (z >= 0.0f) && (x <= 1.0f) && (y <= 1.0f) && (z <= 1.0f), "Interpolation input out of 0.0 to 1.0 range."); diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl index d6f112d6..76d4049a 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolume.inl @@ -90,7 +90,7 @@ namespace PolyVox template LargeVolume::LargeVolume(const LargeVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -112,7 +112,7 @@ namespace PolyVox template LargeVolume& LargeVolume::operator=(const LargeVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -124,6 +124,7 @@ namespace PolyVox template VoxelType LargeVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; @@ -228,7 +229,7 @@ namespace PolyVox default: { //Should never happen - POLYVOX_ASSERT(false, "Invlaid case."); + POLYVOX_THROW(std::invalid_argument, "Wrap mode parameter has an unrecognised value."); return VoxelType(); } } @@ -282,6 +283,7 @@ namespace PolyVox template bool LargeVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; @@ -445,13 +447,8 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// template void LargeVolume::initialise(const Region& regValidRegion, uint16_t uBlockSideLength) - { - //Debug mode validation - POLYVOX_ASSERT(uBlockSideLength > 0, "Block side length cannot be zero."); - POLYVOX_ASSERT(isPowerOf2(uBlockSideLength), "Block side length must be a power of two."); - POLYVOX_ASSERT(m_pCompressor, "You must provide a compressor for the LargeVolume to use."); - - //Release mode validation + { + //Validate parameters if(uBlockSideLength == 0) { POLYVOX_THROW(std::invalid_argument, "Block side length cannot be zero."); diff --git a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl b/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl index ee9e4a2c..9c05bc9d 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LargeVolumeSampler.inl @@ -143,7 +143,7 @@ namespace PolyVox }*/ //Need to think what effect this has on any existing iterators. - POLYVOX_ASSERT(false, "This function cnnot be used on LargeVolume samplers."); + POLYVOX_THROW(not_implemented, "This function cannot be used on LargeVolume samplers."); return false; } diff --git a/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl b/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl index e6093723..4d942519 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/LowPassFilter.inl @@ -39,14 +39,15 @@ namespace PolyVox ,m_uKernelSize(uKernelSize) { //Kernel size must be at least three - POLYVOX_ASSERT(m_uKernelSize >= 3, "Kernel size must be at least three"); - m_uKernelSize = std::max(m_uKernelSize, static_cast(3)); //For release builds + if(m_uKernelSize < 3) + { + POLYVOX_THROW(std::invalid_argument, "Kernel size must be at least three"); + } //Kernel size must be odd - POLYVOX_ASSERT(m_uKernelSize % 2 == 1, "Kernel size must be odd"); - if(m_uKernelSize % 2 == 0) //For release builds + if(m_uKernelSize % 2 == 0) { - m_uKernelSize++; + POLYVOX_THROW(std::invalid_argument, "Kernel size must be odd"); } } diff --git a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl index 36023c5b..c6face08 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/RawVolume.inl @@ -47,7 +47,7 @@ namespace PolyVox template RawVolume::RawVolume(const RawVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ namespace PolyVox template RawVolume& RawVolume::operator=(const RawVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -82,6 +82,7 @@ namespace PolyVox template VoxelType RawVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const Vector3DInt32& v3dLowerCorner = this->m_regValidRegion.getLowerCorner(); @@ -186,7 +187,7 @@ namespace PolyVox default: { //Should never happen - POLYVOX_ASSERT(false, "Invalid case."); + POLYVOX_THROW(std::invalid_argument, "Wrap mode parameter has an unrecognised value."); return VoxelType(); } } @@ -254,10 +255,18 @@ namespace PolyVox { this->m_regValidRegion = regValidRegion; - //Ensure dimensions of the specified Region are valid - POLYVOX_ASSERT(this->getWidth() > 0, "Volume width must be greater than zero."); - POLYVOX_ASSERT(this->getHeight() > 0, "Volume width must be greater than zero."); - POLYVOX_ASSERT(this->getDepth() > 0, "Volume width must be greater than zero."); + if(this->getWidth() <= 0) + { + POLYVOX_THROW(std::invalid_argument, "Volume width must be greater than zero."); + } + if(this->getHeight() <= 0) + { + POLYVOX_THROW(std::invalid_argument, "Volume height must be greater than zero."); + } + if(this->getDepth() <= 0) + { + POLYVOX_THROW(std::invalid_argument, "Volume depth must be greater than zero."); + } //Create the data m_pData = new VoxelType[this->getWidth() * this->getHeight()* this->getDepth()]; diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl index b4750ca0..573f3c83 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolume.inl @@ -48,7 +48,7 @@ namespace PolyVox template SimpleVolume::SimpleVolume(const SimpleVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Copy constructor not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume copy constructor not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ namespace PolyVox template SimpleVolume& SimpleVolume::operator=(const SimpleVolume& /*rhs*/) { - POLYVOX_ASSERT(false, "Assignment operator not implemented."); // See function comment above. + POLYVOX_THROW(not_implemented, "Volume assignment operator not implemented for performance reasons."); } //////////////////////////////////////////////////////////////////////////////// @@ -82,6 +82,7 @@ namespace PolyVox template VoxelType SimpleVolume::getVoxel(int32_t uXPos, int32_t uYPos, int32_t uZPos) const { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; @@ -186,7 +187,7 @@ namespace PolyVox default: { //Should never happen - POLYVOX_ASSERT(false, "Invalid case."); + POLYVOX_THROW(std::invalid_argument, "Wrap mode parameter has an unrecognised value."); return VoxelType(); } } @@ -212,6 +213,7 @@ namespace PolyVox template bool SimpleVolume::setVoxelAt(int32_t uXPos, int32_t uYPos, int32_t uZPos, VoxelType tValue) { + // PolyVox does not throw an exception when a voxel is out of range. Please see 'Error Handling' in the User Manual. POLYVOX_ASSERT(this->m_regValidRegion.containsPoint(Vector3DInt32(uXPos, uYPos, uZPos)), "Position is outside valid region"); const int32_t blockX = uXPos >> m_uBlockSideLengthPower; @@ -246,12 +248,7 @@ namespace PolyVox //////////////////////////////////////////////////////////////////////////////// template void SimpleVolume::initialise(const Region& regValidRegion, uint16_t uBlockSideLength) - { - //Debug mode validation - POLYVOX_ASSERT(uBlockSideLength >= 8, "Block side length should be at least 8"); - POLYVOX_ASSERT(uBlockSideLength <= 256, "Block side length should not be more than 256"); - POLYVOX_ASSERT(isPowerOf2(uBlockSideLength), "Block side length must be a power of two."); - + { //Release mode validation if(uBlockSideLength < 8) { diff --git a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl index c65ca4cb..08e06c1e 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/SimpleVolumeBlock.inl @@ -52,10 +52,10 @@ namespace PolyVox template VoxelType SimpleVolume::Block::getVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos) const { + // This is internal code not directly called by the user. For efficiency we assert rather than throwing. POLYVOX_ASSERT(uXPos < m_uSideLength, "Position is outside of the block."); POLYVOX_ASSERT(uYPos < m_uSideLength, "Position is outside of the block."); POLYVOX_ASSERT(uZPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data available"); return m_tUncompressedData @@ -75,10 +75,10 @@ namespace PolyVox template void SimpleVolume::Block::setVoxelAt(uint16_t uXPos, uint16_t uYPos, uint16_t uZPos, VoxelType tValue) { + // This is internal code not directly called by the user. For efficiency we assert rather than throwing. POLYVOX_ASSERT(uXPos < m_uSideLength, "Position is outside of the block."); POLYVOX_ASSERT(uYPos < m_uSideLength, "Position is outside of the block."); POLYVOX_ASSERT(uZPos < m_uSideLength, "Position is outside of the block."); - POLYVOX_ASSERT(m_tUncompressedData, "No uncompressed data available"); m_tUncompressedData @@ -105,9 +105,6 @@ namespace PolyVox template void SimpleVolume::Block::initialise(uint16_t uSideLength) { - //Debug mode validation - POLYVOX_ASSERT(isPowerOf2(uSideLength), "Block side length must be a power of two."); - //Release mode validation if(!isPowerOf2(uSideLength)) { diff --git a/library/PolyVoxCore/include/PolyVoxCore/Vector.inl b/library/PolyVoxCore/include/PolyVoxCore/Vector.inl index 57b3e2f0..a877fea0 100644 --- a/library/PolyVoxCore/include/PolyVoxCore/Vector.inl +++ b/library/PolyVoxCore/include/PolyVoxCore/Vector.inl @@ -414,7 +414,11 @@ namespace PolyVox template inline StorageType Vector::getElement(uint32_t index) const { - POLYVOX_ASSERT(index < Size, "Attempted to access invalid vector element."); + if(index >= Size) + { + POLYVOX_THROW(std::out_of_range, "Attempted to access invalid vector element."); + } + return m_tElements[index]; } @@ -465,7 +469,11 @@ namespace PolyVox template inline void Vector::setElement(uint32_t index, StorageType tValue) { - POLYVOX_ASSERT(index < Size, "Attempted to access invalid vector element."); + if(index >= Size) + { + POLYVOX_THROW(std::out_of_range, "Attempted to access invalid vector element."); + } + m_tElements[index] = tValue; } @@ -645,10 +653,17 @@ namespace PolyVox inline void Vector::normalise(void) { float fLength = this->length(); + if(fLength <= 0.0) + { + POLYVOX_THROW(invalid_operation, "Cannot normalise a vector with a length of zero"); + } + for(uint32_t ct = 0; ct < Size; ++ct) { // Standard float rules apply for divide-by-zero m_tElements[ct] /= fLength; + + //This shouldn't happen as we had the length check earlier. So it's probably a bug if it does happen. POLYVOX_ASSERT(m_tElements[ct] == m_tElements[ct], "Obtained NAN during vector normalisation. Perhaps the input vector was too short?"); } } diff --git a/library/PolyVoxCore/source/Impl/Utility.cpp b/library/PolyVoxCore/source/Impl/Utility.cpp index c82c9630..a035b115 100644 --- a/library/PolyVoxCore/source/Impl/Utility.cpp +++ b/library/PolyVoxCore/source/Impl/Utility.cpp @@ -30,10 +30,6 @@ namespace PolyVox //If this is not the case then the output is undefined. uint8_t logBase2(uint32_t uInput) { - //Debug mode validation - POLYVOX_ASSERT(uInput != 0, "Cannot compute the log of zero."); - POLYVOX_ASSERT(isPowerOf2(uInput), "Input must be a power of two in order to compute the log."); - //Release mode validation if(uInput == 0) { diff --git a/library/PolyVoxCore/source/Region.cpp b/library/PolyVoxCore/source/Region.cpp index cfd54869..3db2edd6 100644 --- a/library/PolyVoxCore/source/Region.cpp +++ b/library/PolyVoxCore/source/Region.cpp @@ -78,7 +78,10 @@ namespace PolyVox */ void Region::accumulate(const Region& reg) { - POLYVOX_ASSERT(reg.isValid(), "You cannot accumulate an invalid region."); //The result of accumulating an invalid region is not defined. + if(!reg.isValid()) + { + POLYVOX_THROW(invalid_operation, "You cannot accumulate an invalid region."); //The result of accumulating an invalid region is not defined. + } m_iLowerX = ((std::min)(m_iLowerX, reg.getLowerX())); m_iLowerY = ((std::min)(m_iLowerY, reg.getLowerY()));