2.9. Error Handling
Viskores contains several mechanisms for checking and reporting error conditions.
2.9.1. Runtime Error Exceptions
Viskores uses exceptions to report errors.
All exceptions thrown by Viskores will be a subclass of viskores::cont::Error.
For simple error reporting, it is possible to simply catch a viskores::cont::Error and report the error message string reported by the viskores::cont::Error::GetMessage() method.
1int main(int argc, char** argv)
2{
3 try
4 {
5 // Do something cool with Viskores
6 // ...
7 }
8 catch (const viskores::cont::Error& error)
9 {
10 std::cout << error.GetMessage() << std::endl;
11 return 1;
12 }
13 return 0;
14}
-
class Error : public std::exception
The superclass of all exceptions thrown by any Viskores function or method.
Subclassed by viskores::cont::ErrorBadAllocation, viskores::cont::ErrorBadDevice, viskores::cont::ErrorBadType, viskores::cont::ErrorBadValue, viskores::cont::ErrorExecution, viskores::cont::ErrorFilterExecution, viskores::cont::ErrorInternal, viskores::cont::ErrorUserAbort, viskores::cont::cuda::ErrorCuda, viskores::io::ErrorIO
Public Functions
-
inline const std::string &GetMessage() const
Returns a message describing what caused the error.
-
inline const std::string &GetStackTrace() const
Provides a stack trace to the location where this error was thrown.
-
inline const char *what() const noexcept override
Returns the message for the error and the stack trace for it.
This method is provided for
std::exceptioncompatibility.
-
inline bool GetIsDeviceIndependent() const
Returns true if this exception is device independent.
For exceptions that are not device independent,
viskores::TryExecute, for example, may try executing the code on other available devices.
-
inline const std::string &GetMessage() const
There are several subclasses to viskores::cont::Error.
The specific subclass gives an indication of the type of error that occurred when the exception was thrown.
Catching one of these subclasses may help a program better recover from errors.
-
class ErrorBadAllocation : public viskores::cont::Error
This class is thrown when Viskores attempts to manipulate memory that it should not.
-
class ErrorBadDevice : public viskores::cont::Error
This class is thrown when Viskores performs an operation that is not supported on the current device.
-
class ErrorBadType : public viskores::cont::Error
This class is thrown when Viskores encounters data of a type that is incompatible with the current operation.
-
class ErrorBadValue : public viskores::cont::Error
This class is thrown when a Viskores function or method encounters an invalid value that inhibits progress.
-
class ErrorExecution : public viskores::cont::Error
This class is thrown in the control environment whenever an error occurs in the execution environment.
-
class ErrorFilterExecution : public viskores::cont::Error
This class is primarily intended to filters to throw in the control environment to indicate an execution failure due to misconfiguration e.g.
incorrect parameters, etc. This is a device independent exception i.e. when thrown, unlike most other exceptions, Viskores will not try to re-execute the filter on another available device.
-
class ErrorInternal : public viskores::cont::Error
This class is thrown when Viskores detects an internal state that should never be reached.
This error usually indicates a bug in viskores or, at best, Viskores failed to detect an invalid input it should have.
2.9.2. Asserting Conditions
In addition to the aforementioned error signaling, the viskores/Assert.h header file defines a macro named VISKORES_ASSERT.
This macro behaves the same as the POSIX assert macro.
It takes a single argument that is a condition that is expected to be true.
If it is not true, the program is halted and a message is printed.
Asserts are useful debugging tools to ensure that software is behaving and being used as expected.
-
VISKORES_ASSERT(condition)
Asserts that condition resolves to true.
If condition is false, then a diagnostic message is outputted and execution is terminated. The behavior is essentially the same as the POSIX assert macro, but is wrapped for added portability.
Like the POSIX assert macro, the check will be removed when compiling in non-debug mode (specifically when NDEBUG is defined), so be prepared for the possibility that the condition is never evaluated.
The VISKORES_NO_ASSERT cmake and preprocessor option allows debugging builds to remove assertions for performance reasons.
1template<typename T>
2VISKORES_CONT T GetArrayValue(viskores::cont::ArrayHandle<T> arrayHandle,
3 viskores::Id index)
4{
5 VISKORES_ASSERT(index >= 0);
6 VISKORES_ASSERT(index < arrayHandle.GetNumberOfValues());
Did You Know?
Like the POSIX assert, if the NDEBUG macro is defined, then VISKORES_ASSERT will become an empty expression.
Typically NDEBUG is defined with a compiler flag (like -DNDEBUG) for release builds to better optimize the code.
CMake will automatically add this flag for release builds.
Common Errors
A helpful warning provided by many compilers alerts you of unused variables.
(This warning is commonly enabled on Viskores regression test nightly builds.)
If a function argument is used only in a VISKORES_ASSERT, then it will be required for debug builds and be unused in release builds.
To get around this problem, add a statement to the function of the form (void)variableName;.
This statement will have no effect on the code generated but will suppress the warning for release builds.
2.9.3. Compile Time Checks
Because Viskores makes heavy use of C++ templates, it is possible that these templates could be used with inappropriate types in the arguments.
Using an unexpected type in a template can lead to very confusing errors, so it is better to catch such problems as early as possible.
The VISKORES_STATIC_ASSERT macro, defined in viskores/StaticAssert.h makes this possible.
This macro takes a constant expression that can be evaluated at compile time and verifies that the result is true.
In the following example, VISKORES_STATIC_ASSERT and its sister macro VISKORES_STATIC_ASSERT_MSG, which allows you to give a descriptive message for the failure, are used to implement checks on a templated function that is designed to work on any scalar type that is represented by 32 or more bits.
1template<typename T>
2VISKORES_EXEC_CONT void MyMathFunction(T& value)
3{
4 VISKORES_STATIC_ASSERT(
5 (std::is_same<typename viskores::TypeTraits<T>::DimensionalityTag,
6 viskores::TypeTraitsScalarTag>::value));
7
8 VISKORES_STATIC_ASSERT_MSG(sizeof(T) >= 4,
9 "MyMathFunction needs types with at least 32 bits.");
Did You Know?
In addition to the several trait template classes provided by Viskores to introspect C++ types, the C++ standard type_traits header file contains several helpful templates for general queries on types.
Example 2.64 demonstrates the use of one such template: std::is_same.
Common Errors
Many templates used to introspect types resolve to the tags std::true_type and std::false_type rather than the constant values true and false that VISKORES_STATIC_ASSERT expects.
The std::true_type and std::false_type tags can be converted to the Boolean literal by adding ::value to the end of them.
Failing to do so will cause VISKORES_STATIC_ASSERT to behave incorrectly.
Example 2.64 demonstrates getting the Boolean literal from the result of std::is_same.