commit 2e6c26a92e2b3d5210c7d67fc2650f18efd29634
parent 9df24c55285dd6eb23491cb7e06b35aefc705be4
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date: Fri, 31 Mar 2017 23:22:16 +0300
Testo: add ASSERT, refactor comparisons
Diffstat:
4 files changed, 303 insertions(+), 223 deletions(-)
diff --git a/include/kfr/testo/assert.hpp b/include/kfr/testo/assert.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "comparison.hpp"
+
+#if defined(CMT_COMPILER_MSVC)
+#include <intrin.h>
+#define TESTO_BREAKPOINT __debugbreak()
+#else
+#if defined(__i386__) || defined(__x86_64__)
+#define TESTO_BREAKPOINT __asm__ __volatile__("int $0x03")
+#else
+#define TESTO_BREAKPOINT __builtin_trap()
+#endif
+#endif
+
+namespace testo
+{
+
+inline void assertion_failed(const std::string& string, const char* file, int line)
+{
+ errorln("Assertion failed at ", file, ":", line);
+ errorln(string);
+ errorln();
+}
+
+template <typename Op, typename L, typename R>
+bool check_assertion(const comparison<Op, L, R>& comparison, const char* expr, const char* file, int line)
+{
+ bool result = comparison();
+ if (!result)
+ {
+ assertion_failed(
+ as_string(padleft(22, expr), " | ", comparison.left, " ", Op::op(), " ", comparison.right), file,
+ line);
+ }
+ return result;
+}
+
+template <typename L>
+bool check_assertion(const half_comparison<L>& comparison, const char* expr, const char* file, int line)
+{
+ bool result = static_cast<bool>(comparison.left);
+ if (!result)
+ {
+ assertion_failed(as_string(padleft(22, expr), " | ", comparison.left), file, line);
+ }
+ return false;
+}
+
+#if defined(TESTO_ASSERTION_ON) || !(defined(NDEBUG) || defined(TESTO_ASSERTION_OFF))
+
+#define TESTO_ASSERT(...) \
+ do \
+ { \
+ if (!::testo::check_assertion(::testo::make_comparison() <= __VA_ARGS__, #__VA_ARGS__, __FILE__, \
+ __LINE__)) \
+ TESTO_BREAKPOINT; \
+ } while (0)
+
+#else
+#define TESTO_ASSERT(...) \
+ do \
+ { \
+ } while (false && (__VA_ARGS__))
+#endif
+
+#ifndef TESTO_NO_SHORT_MACROS
+#define ASSERT TESTO_ASSERT
+#endif
+}
diff --git a/include/kfr/testo/comparison.hpp b/include/kfr/testo/comparison.hpp
@@ -0,0 +1,225 @@
+#pragma once
+
+#include "../cometa/tuple.hpp"
+
+#include "../cometa.hpp"
+#include "../cometa/range.hpp"
+#include "../cometa/string.hpp"
+#include <cmath>
+
+CMT_PRAGMA_GNU(GCC diagnostic push)
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wexit-time-destructors")
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wpadded")
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wshadow")
+
+namespace testo
+{
+
+using namespace cometa;
+
+template <typename Fn, typename L, typename R>
+struct comparison
+{
+ L left;
+ R right;
+ Fn cmp;
+
+ comparison(L&& left, R&& right) : left(std::forward<L>(left)), right(std::forward<R>(right)) {}
+
+ bool operator()() const { return cmp(left, right); }
+};
+
+template <typename Left, typename Right>
+struct static_assert_type_eq
+{
+ static_assert(std::is_same<Left, Right>::value, "std::is_same<Left, Right>::value");
+};
+
+template <typename T, T left, T right>
+struct static_assert_eq
+{
+ static_assert(left == right, "left == right");
+};
+
+template <typename L, typename R, typename = void>
+struct equality_comparer
+{
+ bool operator()(const L& l, const R& r) const { return l == r; }
+};
+
+CMT_PRAGMA_GNU(GCC diagnostic push)
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wfloat-equal")
+
+template <typename T>
+inline T& epsilon()
+{
+ static T value = std::numeric_limits<T>::epsilon();
+ return value;
+}
+
+template <>
+struct equality_comparer<float, float>
+{
+ bool operator()(const float& l, const float& r) const { return !(std::abs(l - r) > epsilon<float>()); }
+};
+template <>
+struct equality_comparer<double, double>
+{
+ bool operator()(const double& l, const double& r) const { return !(std::abs(l - r) > epsilon<double>()); }
+};
+template <>
+struct equality_comparer<long double, long double>
+{
+ bool operator()(const long double& l, const long double& r) const
+ {
+ return !(std::abs(l - r) > epsilon<long double>());
+ }
+};
+
+CMT_PRAGMA_GNU(GCC diagnostic pop)
+
+template <typename L, typename R>
+struct equality_comparer<L, R, void_t<enable_if<!compound_type_traits<L>::is_scalar>>>
+{
+ using Tsubtype = subtype<L>;
+ constexpr static static_assert_type_eq<subtype<L>, subtype<R>> assert{};
+
+ bool operator()(const L& l, const R& r) const
+ {
+ if (compound_type_traits<L>::width != compound_type_traits<R>::width)
+ return false;
+
+ compound_type_traits<L> itl;
+ compound_type_traits<R> itr;
+ for (size_t i = 0; i < compound_type_traits<L>::width; i++)
+ {
+ equality_comparer<Tsubtype, Tsubtype> cmp;
+ if (!cmp(itl.at(l, i), itr.at(r, i)))
+ return false;
+ }
+ return true;
+ }
+};
+
+struct cmp_eq
+{
+ static const char* op() { return "=="; }
+
+ template <typename L, typename R>
+ bool operator()(L&& left, R&& right) const
+ {
+ equality_comparer<decay<L>, decay<R>> eq;
+ return eq(left, right);
+ }
+};
+
+struct cmp_ne
+{
+ static const char* op() { return "!="; }
+
+ template <typename L, typename R>
+ bool operator()(L&& left, R&& right) const
+ {
+ return !cmp_eq()(left, right);
+ }
+};
+
+struct cmp_lt
+{
+ static const char* op() { return "<"; }
+
+ template <typename L, typename R>
+ bool operator()(L&& left, R&& right) const
+ {
+ return left < right;
+ }
+};
+
+struct cmp_gt
+{
+ static const char* op() { return ">"; }
+
+ template <typename L, typename R>
+ bool operator()(L&& left, R&& right) const
+ {
+ return left > right;
+ }
+};
+
+struct cmp_le
+{
+ static const char* op() { return "<="; }
+
+ template <typename L, typename R>
+ bool operator()(L&& left, R&& right) const
+ {
+ return left <= right;
+ }
+};
+
+struct cmp_ge
+{
+ static const char* op() { return ">="; }
+
+ template <typename L, typename R>
+ bool operator()(L&& left, R&& right) const
+ {
+ return left >= right;
+ }
+};
+
+template <typename L>
+struct half_comparison
+{
+ half_comparison(L&& left) : left(std::forward<L>(left)) {}
+
+ template <typename R>
+ comparison<cmp_eq, L, R> operator==(R&& right)
+ {
+ return comparison<cmp_eq, L, R>(std::forward<L>(left), std::forward<R>(right));
+ }
+
+ template <typename R>
+ comparison<cmp_ne, L, R> operator!=(R&& right)
+ {
+ return comparison<cmp_ne, L, R>(std::forward<L>(left), std::forward<R>(right));
+ }
+
+ template <typename R>
+ comparison<cmp_lt, L, R> operator<(R&& right)
+ {
+ return comparison<cmp_lt, L, R>(std::forward<L>(left), std::forward<R>(right));
+ }
+
+ template <typename R>
+ comparison<cmp_gt, L, R> operator>(R&& right)
+ {
+ return comparison<cmp_gt, L, R>(std::forward<L>(left), std::forward<R>(right));
+ }
+
+ template <typename R>
+ comparison<cmp_le, L, R> operator<=(R&& right)
+ {
+ return comparison<cmp_le, L, R>(std::forward<L>(left), std::forward<R>(right));
+ }
+
+ template <typename R>
+ comparison<cmp_ge, L, R> operator>=(R&& right)
+ {
+ return comparison<cmp_ge, L, R>(std::forward<L>(left), std::forward<R>(right));
+ }
+
+ L left;
+};
+
+struct make_comparison
+{
+ template <typename L>
+ half_comparison<L> operator<=(L&& left)
+ {
+ return half_comparison<L>(std::forward<L>(left));
+ }
+};
+}
+
+CMT_PRAGMA_GNU(GCC diagnostic pop)
diff --git a/include/kfr/testo/testo.hpp b/include/kfr/testo/testo.hpp
@@ -1,10 +1,6 @@
#pragma once
-#include "../cometa/tuple.hpp"
-
-#include "../cometa.hpp"
-#include "../cometa/range.hpp"
-#include "../cometa/string.hpp"
+#include "comparison.hpp"
#include <algorithm>
#include <ctime>
@@ -20,19 +16,10 @@
#include <chrono>
#include <cmath>
-#if !defined CLANG_DIAGNOSTIC_PRAGMA
-#if defined __clang__
-#define TESTO_STRING(str) #str
-#define CLANG_DIAGNOSTIC_PRAGMA(pragma) _Pragma(TESTO_STRING(clang diagnostic pragma))
-#else
-#define CLANG_DIAGNOSTIC_PRAGMA(pragma)
-#endif
-#endif
-
-CLANG_DIAGNOSTIC_PRAGMA(push)
-CLANG_DIAGNOSTIC_PRAGMA(ignored "-Wexit-time-destructors")
-CLANG_DIAGNOSTIC_PRAGMA(ignored "-Wpadded")
-CLANG_DIAGNOSTIC_PRAGMA(ignored "-Wshadow")
+CMT_PRAGMA_GNU(GCC diagnostic push)
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wexit-time-destructors")
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wpadded")
+CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wshadow")
namespace testo
{
@@ -90,210 +77,6 @@ inline double ulp_distance(long double reference, T test)
using namespace console_colors;
-template <typename Fn, typename L, typename R>
-struct comparison
-{
- L left;
- R right;
- Fn cmp;
-
- comparison(L&& left, R&& right) : left(std::forward<L>(left)), right(std::forward<R>(right)) {}
-
- bool operator()() const { return cmp(left, right); }
-};
-
-template <typename Left, typename Right>
-struct static_assert_type_eq
-{
- static_assert(std::is_same<Left, Right>::value, "std::is_same<Left, Right>::value");
-};
-
-template <typename T, T left, T right>
-struct static_assert_eq
-{
- static_assert(left == right, "left == right");
-};
-
-template <typename L, typename R, typename = void>
-struct equality_comparer
-{
- bool operator()(const L& l, const R& r) const { return l == r; }
-};
-
-CMT_PRAGMA_GNU(GCC diagnostic push)
-CMT_PRAGMA_GNU(GCC diagnostic ignored "-Wfloat-equal")
-
-template <typename T>
-inline T& epsilon()
-{
- static T value = std::numeric_limits<T>::epsilon();
- return value;
-}
-
-template <>
-struct equality_comparer<float, float>
-{
- bool operator()(const float& l, const float& r) const { return !(std::abs(l - r) > epsilon<float>()); }
-};
-template <>
-struct equality_comparer<double, double>
-{
- bool operator()(const double& l, const double& r) const { return !(std::abs(l - r) > epsilon<double>()); }
-};
-template <>
-struct equality_comparer<long double, long double>
-{
- bool operator()(const long double& l, const long double& r) const
- {
- return !(std::abs(l - r) > epsilon<long double>());
- }
-};
-
-CMT_PRAGMA_GNU(GCC diagnostic pop)
-
-template <typename L, typename R>
-struct equality_comparer<L, R, void_t<enable_if<!compound_type_traits<L>::is_scalar>>>
-{
- using Tsubtype = subtype<L>;
- constexpr static static_assert_type_eq<subtype<L>, subtype<R>> assert{};
-
- bool operator()(const L& l, const R& r) const
- {
- if (compound_type_traits<L>::width != compound_type_traits<R>::width)
- return false;
-
- compound_type_traits<L> itl;
- compound_type_traits<R> itr;
- for (size_t i = 0; i < compound_type_traits<L>::width; i++)
- {
- equality_comparer<Tsubtype, Tsubtype> cmp;
- if (!cmp(itl.at(l, i), itr.at(r, i)))
- return false;
- }
- return true;
- }
-};
-
-struct cmp_eq
-{
- static const char* op() { return "=="; }
-
- template <typename L, typename R>
- bool operator()(L&& left, R&& right) const
- {
- equality_comparer<decay<L>, decay<R>> eq;
- return eq(left, right);
- }
-};
-
-struct cmp_ne
-{
- static const char* op() { return "!="; }
-
- template <typename L, typename R>
- bool operator()(L&& left, R&& right) const
- {
- return !cmp_eq()(left, right);
- }
-};
-
-struct cmp_lt
-{
- static const char* op() { return "<"; }
-
- template <typename L, typename R>
- bool operator()(L&& left, R&& right) const
- {
- return left < right;
- }
-};
-
-struct cmp_gt
-{
- static const char* op() { return ">"; }
-
- template <typename L, typename R>
- bool operator()(L&& left, R&& right) const
- {
- return left > right;
- }
-};
-
-struct cmp_le
-{
- static const char* op() { return "<="; }
-
- template <typename L, typename R>
- bool operator()(L&& left, R&& right) const
- {
- return left <= right;
- }
-};
-
-struct cmp_ge
-{
- static const char* op() { return ">="; }
-
- template <typename L, typename R>
- bool operator()(L&& left, R&& right) const
- {
- return left >= right;
- }
-};
-
-template <typename L>
-struct half_comparison
-{
- half_comparison(L&& left) : left(std::forward<L>(left)) {}
-
- template <typename R>
- comparison<cmp_eq, L, R> operator==(R&& right)
- {
- return comparison<cmp_eq, L, R>(std::forward<L>(left), std::forward<R>(right));
- }
-
- template <typename R>
- comparison<cmp_ne, L, R> operator!=(R&& right)
- {
- return comparison<cmp_ne, L, R>(std::forward<L>(left), std::forward<R>(right));
- }
-
- template <typename R>
- comparison<cmp_lt, L, R> operator<(R&& right)
- {
- return comparison<cmp_lt, L, R>(std::forward<L>(left), std::forward<R>(right));
- }
-
- template <typename R>
- comparison<cmp_gt, L, R> operator>(R&& right)
- {
- return comparison<cmp_gt, L, R>(std::forward<L>(left), std::forward<R>(right));
- }
-
- template <typename R>
- comparison<cmp_le, L, R> operator<=(R&& right)
- {
- return comparison<cmp_le, L, R>(std::forward<L>(left), std::forward<R>(right));
- }
-
- template <typename R>
- comparison<cmp_ge, L, R> operator>=(R&& right)
- {
- return comparison<cmp_ge, L, R>(std::forward<L>(left), std::forward<R>(right));
- }
-
- L left;
-};
-
-struct make_comparison
-{
- template <typename L>
- half_comparison<L> operator<=(L&& left)
- {
- return half_comparison<L>(std::forward<L>(left));
- }
-};
-
inline std::vector<std::string> split(const std::string& text, char delimeter)
{
std::string r = text;
@@ -562,4 +345,4 @@ void assert_is_same_decay()
#endif
}
-CLANG_DIAGNOSTIC_PRAGMA(pop)
+CMT_PRAGMA_GNU(GCC diagnostic pop)
diff --git a/sources.cmake b/sources.cmake
@@ -105,4 +105,6 @@ set(
${PROJECT_SOURCE_DIR}/include/kfr/io/python_plot.hpp
${PROJECT_SOURCE_DIR}/include/kfr/io/tostring.hpp
${PROJECT_SOURCE_DIR}/include/kfr/testo/testo.hpp
+ ${PROJECT_SOURCE_DIR}/include/kfr/testo/assert.hpp
+ ${PROJECT_SOURCE_DIR}/include/kfr/testo/comparison.hpp
)