Skip to content

Exceptions

Maxime ROUFFET edited this page Mar 29, 2024 · 16 revisions

Default

The Default exception type only needs a predicate and will throw on failure.
The predicate will automatically be logged as a string in the message.

SA_ASSERT((Default, MyInit()), MyChannel, "MyAPI initialization failed");
[17:3:11][0] {AssertFailure - MyChannel}   main.cpp:37 - int main()
Msg:    MyInit()        evaluated to false!
Dets:   MyAPI initialization failed

See unit tests for more examples.

Equals

The Equals exception is used to ensure equality (throw on failure aka NOT equals).
LHS and RHS arguments must be provided. Their names and values will automatically be logged as a string.

SA_ASSERT((Equals, myInt1, myInt2), MyChannel, "My details");
[17:4:4][0] {AssertFailure - MyChannel}    main.cpp:38 - int main()
Msg:    'myInt1' == 'myInt2' => { 1 == 2 }: Values must be equal.
Dets:   My details

For float and double parameters, a 'NearlyEquals' is performed using std::numeric_limits<T>::epsilon().

See unit tests for more examples.

Equals0

Specialization for friendly equals 0 checks.

SA_ASSERT((Equals0, myInt1), MyChannel, "My details");
[17:8:41][0] {AssertFailure - MyChannel}   main.cpp:43 - int main()
Msg:    myInt1 == i() => { 1 == 0 }: Values must be equal.
Dets:   My details

See unit tests for more examples.

Equals1

Specialization for friendly equals 1 checks.

SA_ASSERT((Equals1, myInt1), MyChannel, "My details");
[17:9:36][0] {AssertSuccess - MyChannel}   main.cpp:43 - int main()
Msg:    myInt1 == i(1) => { 1 == 1 }: Values must be equal.
Dets:   My details

See unit tests for more examples.

NotEquals

The NotEquals exception is used to ensure non-equality (throw on failure aka EQUALS).
LHS and RHS arguments must be provided. Their names and values will automatically be logged as a string.

SA_ASSERT((NotEquals, myInt1, myInt2), MyChannel, "My details");
[17:4:4][0] {AssertSuccess - MyChannel}    main.cpp:38 - int main()
Msg:    'myInt1' != 'myInt2' => { 1 != 2 }: Values must be NOT equal.
Dets:   My details

For float and double parameters, a 'NearlyEquals' is performed using std::numeric_limits<T>::epsilon().

See unit tests for more examples.

NotEquals0

Specialization for friendly not equals 0 checks.

SA_ASSERT((NotEquals0, myInt1), MyChannel, "My details");
[17:8:41][0] {AssertSuccess - MyChannel}   main.cpp:43 - int main()
Msg:    myInt1 != i() => { 1 != 0 }: Values must be NOT equal.
Dets:   My details

See unit tests for more examples.

NotEquals1

Specialization for friendly not equals 1 checks.

SA_ASSERT((NotEquals1, myInt1), MyChannel, "My details");
[17:9:36][0] {AssertFailure - MyChannel}   main.cpp:43 - int main()
Msg:    myInt1 != i(1) => { 1 != 1 }: Values must be NOT equal.
Dets:   My details

See unit tests for more examples.

Nullptr

The Nullptr exception is used to check the NON-nullity of a pointer. Throw on null pointer provided.

SA_ASSERT((Nullptr, myPtr), MyChannel, "My details");
[17:18:5][0] {AssertFailure - MyChannel}   main.cpp:48 - int main()
Msg:    myPtr must be not null!
Dets:   My details

See unit tests for more examples.

OutOfRange

The OutOfRange exception is used to check index access range.
currIndex (index to access), minBound, and maxBound arguments must be provided. Their names and values will automatically be logged.

SA_ASSERT((OutOfRange, myIndex, minBound, maxBound), MyChannel, "My details");
[17:22:25][0] {AssertFailure - MyChannel}  main.cpp:53 - int main()
Msg:    Index 'myIndex' [9] is out of range ['minBound';'maxBound'] => [1;7]
Dets:   My details

See unit tests for more examples.

OutOfArrayRange

The OutOfArrayRange exception is used to check index access range within an array.
Any array type can be provided, however the size() method must be implemented.
currIndex (index to access) and array must be provided. Additional minBound and maxBound within the array size can be provided. All their names and values will automatically be logged.

int myIndex = 1;
std::vector<int> myVector = {1, 5, 3, 8, 6, 2};
SA_ASSERT((OutOfArrayRange, myIndex, myVector), MyChannel, "My details");
SA_ASSERT((OutOfArrayRange, myIndex, myVector, 2, 4), MyChannel, "My details");
[17:27:25][0] {AssertSuccess - MyChannel}  main.cpp:58 - int main()
Msg:    Index 'myIndex' [1] must be in array 'myVector' range [0;5]
Dets:   My details

[17:27:25][0] {AssertFailure - MyChannel}  main.cpp:59 - int main()
Msg:    Index 'myIndex' [1] must be in array 'myVector' range [2;4]
Dets:   My details

terminate called after throwing an instance of 'SA::Exception_OutOfArrayRange'

See unit tests for more examples.

Custom Exception

The exception system can easily be extended by creating a custom exception type and providing a construction-helper macro.
First, start by defining the new exception class.

#include <SA/Logger/Exceptions/Exception.hpp>

namespace MyNamespace
{
    // Custom Exception class.
    class MyException : public SA::Exception
    {
    public:
        int i = 0;
        float f = 0.0f;

        MyException(
            BaseInfo _info,              // Required info for Exception system.
            int _i,                      // 'i' Value
            std::wstring _iStr,          // Input 'i' variable name.
            float _f,                    // 'f' Value
            std::wstring _fStr,          // Input 'f' variable name.
        )  noexcept :
            Exception(
                std::move(_info),
                _i >= 2 * _f,            // Predicate
                SA::StringFormat(L"\'%1\' >= 2 * \'%2\' => { %3 >= 2 * %4 }: evaluated to false!", _iStr, _fStr, _i, _f), // Message
            ),

            // Save input parameters' value.
            i{ _i },
            f{ _f }
        {
        }
    };
} // MyNamespace

Then, below the class definition, add the constructor-helper macro definition with the name __SA_CREATE_EXCEPTION_<exception_type_name>.
This allow the addition of preprocessed arguments for the exception construction (such as variable names).

/// Define constructor-helper macro.
#define __SA_CREATE_EXCEPTION_MyException(_baseInfo, _i, _f) MyNamespace::MyException(\
    _baseInfo,\
    _i,\
    SA_WSTR(_i),\
    _f,\
    SA_WSTR(_f)\
)

This macro will receive the _params... from the SA_ASSERT macro and is made to generate additional custom parameters to build the exception (ex: get input parameters' name). The _baseInfo will always be automatically provided and must be forwarded to the exception.
/!\ Warning: If the exception type is defined in a namespace, it must be specified within the constructor-helper macro (MyNamespace::MyException).
After including the new exception type, it is ready to be used with the SA_ASSERT macro:

int myInt = 3;
float myFloat = 2.0f;

try
{
    SA_ASSERT((MyException, myInt, myFloat), TestChan.MyException, L"optional details!");
}
catch (MyException _exc)
{
    // Read values
    _exc.i;
    _exc.f;
}
[18:24:26][0] {AssertFailure - TestChan.MyException}       main.cpp:52 - int __cdecl main(void)
Msg:    'myInt' >= 2 * 'myFloat' => { 3 >= 2 * 2.000000 }: evaluated to false!
Dets:   optional details!