Skip to content

Commit

Permalink
Fix function call in macro argument (#156)
Browse files Browse the repository at this point in the history
* Add unit tests to RoughPy_Core

Introduced unit tests for the RoughPy_Core module by adding a new `src` directory with necessary CMake file and a sample test case. This aims to ensure better code reliability and automated validation of core functionalities.

* Rename string_utilss.h to string_utils.h

Corrected a typo in the header file name from string_utilss.h to string_utils.h to ensure consistency and prevent potential issues with file inclusion. Adjusted CMakeLists.txt accordingly to reflect this change.

* Refactor macros and improve RPY_CHECK messages

Moved and added macros for better preprocessor handling from `macros.h` to a new section. Enhanced RPY_CHECK macro variants in `check.h` to include detailed failure messages for easier debugging.

* Add TODO for customizable check messages

Inserted a TODO comment to highlight the need for customizable messages in RPY_CHECK macros. This will improve clarity and debugging for future checks.

* Fix missing commas in RPY_CHECK macro definitions

Added missing commas in the RPY_CHECK_* macro definitions for better clarity and correctness. This change ensures that the macros are formatted correctly and align with typical macro usage standards.

* Add throw_exception template for detailed error messages

Introduce a templated function `throw_exception` to standardize and improve error reporting. This function concatenates relevant error details such as filename, line number, and function name into a comprehensive message before throwing the specified exception type.

* Set default exception type in throw_exception template.

Changed the throw_exception template to default to std::runtime_error if no exception type is specified. This improves code simplicity and reduces the need for explicit template arguments.

* Add unit tests for RPY_CHECK macros with custom exceptions

This commit introduces new unit tests to verify the behavior of various RPY_CHECK macros when throwing both standard runtime exceptions and specific custom exceptions. These tests ensure that the macros function correctly under different failure conditions.

* Refactor formatting of error checks and debug assertions

Refactored the formatting for the `throw_exception` template function and macros to improve readability. Adjusted the alignment and spacing within the macros and definitions in both `check.h` and `debug_assertion.h` to adhere to consistent code style.
  • Loading branch information
inakleinbottle authored Nov 15, 2024
1 parent a51d92a commit e2b9615
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 37 deletions.
3 changes: 2 additions & 1 deletion core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ add_library(RoughPy_Core INTERFACE
include/roughpy/core/helpers.h
include/roughpy/core/slice.h
include/roughpy/core/smart_ptr.h
include/roughpy/core/strings.h
include/roughpy/core/string_utils.h
)
add_library(RoughPy::Core ALIAS RoughPy_Core)

Expand All @@ -41,3 +41,4 @@ set_target_properties(RoughPy_Core PROPERTIES ROUGHPY_COMPONENT Core)
target_compile_definitions(RoughPy_Core INTERFACE NOMINMAX=1)


add_subdirectory(src)
54 changes: 44 additions & 10 deletions core/include/roughpy/core/check.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include "check_helpers.h"
#include "macros.h"
#include "strings.h"
#include "string_utils.h"

#if defined(RPY_GCC)
# define RPY_FUNC_NAME __PRETTY_FUNCTION__
Expand All @@ -25,9 +25,8 @@

namespace rpy::errors {

template <typename E>
RPY_NO_RETURN
void throw_exception(
template <typename E = std::runtime_error>
RPY_NO_RETURN void throw_exception(
std::string_view user_msg,
const char* filename,
int lineno,
Expand All @@ -48,6 +47,26 @@ void throw_exception(
// boost::stacktrace::stacktrace()));
}

template <typename E = std::runtime_error>
RPY_NO_RETURN void throw_exception(
const char* user_msg,
const char* filename,
int lineno,
const char* func
)
{
throw E(string_cat(
"Error occurred in ",
filename,
" at line ",
lineno,
'(',
func,
"):\n",
user_msg
));
}

}// namespace rpy::errors

#define RPY_THROW_2(EXC_TYPE, MSG) \
Expand Down Expand Up @@ -104,17 +123,32 @@ void throw_exception(
#define RPY_CHECK(...) \
RPY_INVOKE_VA(RPY_CHECK_SEL(RPY_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))

// TODO: Ideally have customisable messages for the these checks

#define RPY_CHECK_EQ(a, b, ...) \
RPY_CHECK(::rpy::compare_equal((a), (b)), __VA_ARGS__)
RPY_CHECK( \
(::rpy::compare_equal((a), (b))), \
"failed check \"" RPY_STRINGIFY((a) == (b)) "\"", __VA_ARGS__)
#define RPY_CHECK_NE(a, b, ...) \
RPY_CHECK(::rpy::compare_not_equal((a), (b)), __VA_ARGS__)
RPY_CHECK( \
(::rpy::compare_not_equal((a), (b))), \
"failed check \"" RPY_STRINGIFY((a) != (b)) "\"", __VA_ARGS__)
#define RPY_CHECK_LT(a, b, ...) \
RPY_CHECK(::rpy::compare_less((a), (b)), __VA_ARGS__)
RPY_CHECK( \
(::rpy::compare_less((a), (b))), \
"failed check \"" RPY_STRINGIFY((a) < (b)) "\"", __VA_ARGS__)
#define RPY_CHECK_LE(a, b, ...) \
RPY_CHECK(::rpy::compare_less_equal((a), (b)), __VA_ARGS__)
RPY_CHECK( \
(::rpy::compare_less_equal((a), (b))), \
"failed check \"" RPY_STRINGIFY((a) <= (b)) "\"", __VA_ARGS__)
#define RPY_CHECK_GT(a, b, ...) \
RPY_CHECK(::rpy::compare_greater((a), (b)), __VA_ARGS__)
RPY_CHECK( \
(::rpy::compare_greater((a), (b))), \
"failed check \"" RPY_STRINGIFY((a) > (b)) "\"", __VA_ARGS__ \
)
#define RPY_CHECK_GE(a, b, ...) \
RPY_CHECK(::rpy::compare_greater_equal((a), (b)), __VA_ARGS__)
RPY_CHECK( \
(::rpy::compare_greater_equal((a), (b))), \
"failed check \"" RPY_STRINGIFY((a) >= (b)) "\"", __VA_ARGS__)

#endif// ROUGHPY_CORE_CHECK_H
29 changes: 12 additions & 17 deletions core/include/roughpy/core/debug_assertion.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@
# undef RPY_DEBUG
#endif


#ifdef RPY_DEBUG
# include "check_helpers.h"
#endif


#ifdef RPY_DEBUG
# if defined(RPY_GCC) || defined(RPY_CLANG)
# define RPY_DBG_ASSERT(ARG) assert(ARG)
Expand All @@ -30,18 +28,15 @@
# define RPY_DBG_ASSERT(ARG) (void) 0
#endif


#define RPY_DBG_ASSERT_EQ(a, b) RPY_DBG_ASSERT(::rpy::compare_equal((a), (b)))
#define RPY_DBG_ASSERT_NE(a, b) RPY_DBG_ASSERT(::rpy::compare_not_equal((a), (b)))
#define RPY_DBG_ASSERT_LT(a, b) RPY_DBG_ASSERT(::rpy::compare_less((a), (b)))
#define RPY_DBG_ASSERT_GT(a, b) RPY_DBG_ASSERT(::rpy::compare_greater((a), (b)))
#define RPY_DBG_ASSERT_LE(a, b) RPY_DBG_ASSERT(::rpy::compare_less_equal((a), (b)))
#define RPY_DBG_ASSERT_GE(a, b) RPY_DBG_ASSERT(::rpy::compare_greater_equal((a), (b)))







#endif //ROUGHPY_CORE_DEBUG_ASSERTION_H
#define RPY_DBG_ASSERT_EQ(a, b) RPY_DBG_ASSERT((::rpy::compare_equal((a), (b))))
#define RPY_DBG_ASSERT_NE(a, b) \
RPY_DBG_ASSERT((::rpy::compare_not_equal((a), (b))))
#define RPY_DBG_ASSERT_LT(a, b) RPY_DBG_ASSERT((::rpy::compare_less((a), (b))))
#define RPY_DBG_ASSERT_GT(a, b) \
RPY_DBG_ASSERT((::rpy::compare_greater((a), (b))))
#define RPY_DBG_ASSERT_LE(a, b) \
RPY_DBG_ASSERT((::rpy::compare_less_equal((a), (b))))
#define RPY_DBG_ASSERT_GE(a, b) \
RPY_DBG_ASSERT((::rpy::compare_greater_equal((a), (b))))

#endif// ROUGHPY_CORE_DEBUG_ASSERTION_H
31 changes: 22 additions & 9 deletions core/include/roughpy/core/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@
# define RPY_HAS_INCLUDE(x) 0
#endif

/*
* The C++ preprocessor is very basic, but one can accomplish fairly advanced
* patterns by exploiting multiple passes of the preprocessor. The following
* macros help with the indirection needed to achieve everything one might want
* to do. One of main consumers of this set of utilities are the RPY_CHECK and
* RPY_DBG_ASSERT macros in Core.
*
* See https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
* and the C++template metaprogramming book by David Abrahams and
* Aleksey Gurtovoy
*/
#define RPY_STRINGIFY_IMPL(ARG) #ARG
#define RPY_STRINGIFY(ARG) RPY_STRINGIFY_IMPL(ARG)

#define RPY_JOIN_IMPL_IMPL(LHS, RHS) LHS##RHS
#define RPY_JOIN_IMPL(LHS, RHS) RPY_JOIN_IMPL_IMPL(LHS, RHS)
#define RPY_JOIN(LHS, RHS) RPY_JOIN_IMPL(LHS, RHS)

#define RPY_IDENTITY(ARG) ARG
#define RPY_EMPTY(...)
#define RPY_DEFER(...) __VA_ARGS__

/*
* MSVC has a bug where it treats __VA_ARGS__ as a single token in argument
* lists. To combat this, we use RPY_INVOKE_VA to invoke the overload macro with
Expand All @@ -72,15 +94,6 @@
#define RPY_COUNT_ARGS(...) \
RPY_INVOKE_VA(RPY_COUNT_ARGS_1, (__VA_ARGS__, 4, 3, 2, 1, 0))

#define RPY_STRINGIFY_IMPL(ARG) #ARG
#define RPY_STRINGIFY(ARG) RPY_STRINGIFY_IMPL(ARG)

#define RPY_JOIN_IMPL_IMPL(LHS, RHS) LHS##RHS
#define RPY_JOIN_IMPL(LHS, RHS) RPY_JOIN_IMPL_IMPL(LHS, RHS)
#define RPY_JOIN(LHS, RHS) RPY_JOIN_IMPL(LHS, RHS)

#define RPY_IDENTITY(ARG) ARG



#if defined(_MSC_VER) && defined(_MSVC_LANG)
Expand Down
File renamed without changes.
19 changes: 19 additions & 0 deletions core/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@




if(ROUGHPY_BUILD_TESTS)


add_executable(test_core
test_check_macros.cpp)


target_include_directories(test_core PRIVATE ${ROUGHPY_CORE_INCLUDE})

target_link_libraries(test_core PRIVATE RoughPy::Core GTest::gtest)

setup_roughpy_cpp_tests(test_core)


endif()
102 changes: 102 additions & 0 deletions core/src/test_check_macros.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// Created by sammorley on 15/11/24.
//




#include <gtest/gtest.h>

#include "check.h"


TEST(CheckMacros, TestCheckFailStandard)
{
EXPECT_THROW(RPY_CHECK(false), std::runtime_error);
}

TEST(CheckMacros, TestCHeckWithSpecifiedException)
{
EXPECT_THROW(
RPY_CHECK(false, "message", std::invalid_argument),
std::invalid_argument
);
}

TEST(CheckMacros, TestCheckEqFails)
{
EXPECT_THROW(RPY_CHECK_EQ(1, 2), std::runtime_error);
}

TEST(CheckMacros, TestCheckEqFailsCustomException)
{
EXPECT_THROW(
RPY_CHECK_EQ(1, 2, std::invalid_argument),
std::invalid_argument
);
}

TEST(CheckMacros, TestCheckNeFails)
{
EXPECT_THROW(RPY_CHECK_NE(1, 1), std::runtime_error);
}

TEST(CheckMacros, TestCheckNeFailsCustomException)
{
EXPECT_THROW(
RPY_CHECK_NE(1, 1, std::invalid_argument),
std::invalid_argument
);
}

TEST(CheckMacros, TestCheckLtFails)
{
EXPECT_THROW(RPY_CHECK_LT(2, 1), std::runtime_error);
}

TEST(CheckMacros, TestCheckLtFailsCustomException)
{
EXPECT_THROW(
RPY_CHECK_LT(2, 1, std::invalid_argument),
std::invalid_argument
);
}

TEST(CheckMacros, TestCheckLeFails)
{
EXPECT_THROW(RPY_CHECK_LE(2, 1), std::runtime_error);
}

TEST(CheckMacros, TestCheckLeFailsCustomException)
{
EXPECT_THROW(
RPY_CHECK_LE(2, 1, std::invalid_argument),
std::invalid_argument
);
}

TEST(CheckMacros, TestCheckGtFails)
{
EXPECT_THROW(RPY_CHECK_GT(1, 2), std::runtime_error);
}

TEST(CheckMacros, TestCheckGtFailsCustomException)
{
EXPECT_THROW(
RPY_CHECK_GT(1, 2, std::invalid_argument),
std::invalid_argument
);
}

TEST(CheckMacros, TestCheckGeFails)
{
EXPECT_THROW(RPY_CHECK_GE(1, 2), std::runtime_error);
}

TEST(CheckMacros, TestCheckGeFailsCustomException)
{
EXPECT_THROW(
RPY_CHECK_GE(1, 2, std::invalid_argument),
std::invalid_argument
);
}

0 comments on commit e2b9615

Please sign in to comment.