diff --git a/include/safe.hpp b/include/safe.hpp index d041942..661d1fa 100644 --- a/include/safe.hpp +++ b/include/safe.hpp @@ -1,12 +1,12 @@ #pragma once -#include - #include - -#include - -#include +#include #include #include -#include \ No newline at end of file +#include + +#include +#include + +#include \ No newline at end of file diff --git a/include/safe/algorithm.hpp b/include/safe/algorithm.hpp new file mode 100644 index 0000000..6acff2b --- /dev/null +++ b/include/safe/algorithm.hpp @@ -0,0 +1,4 @@ +#pragma once + + +#include \ No newline at end of file diff --git a/include/safe/algorithm/accumulate.hpp b/include/safe/algorithm/accumulate.hpp new file mode 100644 index 0000000..5f01400 --- /dev/null +++ b/include/safe/algorithm/accumulate.hpp @@ -0,0 +1,116 @@ +#pragma once + + +#include +#include +#include + +#include + +#include +#include + + +namespace safe { + namespace detail { + template + inline consteval auto fold(auto e, auto op){ + if constexpr (count > 100) { + return safe::dsl::detail::simp( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op( + op(op(op(op(op(op(op(op(op(op(fold(e, op), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e), + e), e), e), e), e), e), e), e), e), e) + ); + + } else if constexpr (count > 10) { + return safe::dsl::detail::simp( + op(op(op(op(op(op(op(op(op(op(fold(e, op), e), e), e), e), e), e), e), e), e), e) + ); + + } else if constexpr (count > 1) { + return safe::dsl::detail::simp(op(fold(e, op), e)); + + } else { + return e; + } + } + + inline constexpr auto plus_op = [](auto a, auto b){return a + b;}; + } + + + template + [[nodiscard]] inline constexpr auto accumulate( + detail::iter_like auto first, + auto last, + auto op + ) { + constexpr auto req = decltype((*first).requirement){}; + constexpr auto sum_req = detail::fold(req, op); + + using ret_num_t = decltype((*first).unsafe_value()); + + auto iter_count = size_t{}; + auto sum = ret_num_t{}; + while ((first != last) && (iter_count < max_iter)) { + sum = op(sum, (*first).unsafe_value()); + first++; + iter_count++; + } + + return safe::var{sum}; + } + + template + [[nodiscard]] inline constexpr auto accumulate( + detail::iter_like auto first, + auto last + ) { + return accumulate(first, last, detail::plus_op); + } + + template + [[nodiscard]] inline constexpr auto accumulate( + detail::range_like auto & range, + auto op + ) { + return accumulate(range.begin(), range.end(), op); + } + + template + [[nodiscard]] inline constexpr auto accumulate( + detail::range_like auto & range + ) { + return accumulate(range.begin(), range.end(), detail::plus_op); + } + + template + [[nodiscard]] inline constexpr auto accumulate( + detail::range_like auto const & range, + auto op + ) { + return accumulate(range.begin(), range.end(), op); + } + + template + [[nodiscard]] inline constexpr auto accumulate(auto const & range) { + return accumulate(range.begin(), range.end(), detail::plus_op); + } +} \ No newline at end of file diff --git a/include/safe/detail/concepts.hpp b/include/safe/detail/concepts.hpp new file mode 100644 index 0000000..a980cfb --- /dev/null +++ b/include/safe/detail/concepts.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace safe::detail { + template + concept iter_like = + requires(I i) { + i++; + *i; + i != i; + }; + + template + concept range_like = + requires(R r) { + {r.begin()} -> iter_like; + r.end(); + }; +} \ No newline at end of file diff --git a/include/safe/detail/integral_type.hpp b/include/safe/detail/integral_type.hpp index 006627e..9768775 100644 --- a/include/safe/detail/integral_type.hpp +++ b/include/safe/detail/integral_type.hpp @@ -1,15 +1,16 @@ #pragma once - +#include #include #include +#include namespace safe::detail { template using integral_type = - var::lowest(), std::numeric_limits::max()> >; diff --git a/include/safe/detail/invoke.hpp b/include/safe/detail/invoke.hpp index bc366cf..9afd280 100644 --- a/include/safe/detail/invoke.hpp +++ b/include/safe/detail/invoke.hpp @@ -37,8 +37,16 @@ namespace safe::detail { } + template + mp_list helper(ReturnT (T::*)(ArgTs...)); + + template + mp_list helper(ReturnT (T::*)(ArgTs...) const); + template - struct function_args; + struct function_args { + using type = decltype(helper(&F::operator())); + }; template struct function_args { diff --git a/include/safe/dsl.hpp b/include/safe/dsl.hpp index 6418dd6..4570dd5 100644 --- a/include/safe/dsl.hpp +++ b/include/safe/dsl.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/include/safe/dsl/bit_width.hpp b/include/safe/dsl/bit_width.hpp new file mode 100644 index 0000000..fc9797f --- /dev/null +++ b/include/safe/dsl/bit_width.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace safe::dsl { + template + struct bit_width_t {}; + + template + struct bit_width_t { + using val = detail::to_mask_t; + + using type = ival_t< + std::bit_width(val::const_bits), + std::bit_width(val::var_bits | val::const_bits) + >; + }; + + template + [[nodiscard]] constexpr auto bit_width(T) + -> bit_width_t + { + return {}; + } +} \ No newline at end of file diff --git a/include/safe/dsl/detail/checked.hpp b/include/safe/dsl/detail/checked.hpp index 371ff46..6c58d2e 100644 --- a/include/safe/dsl/detail/checked.hpp +++ b/include/safe/dsl/detail/checked.hpp @@ -129,7 +129,7 @@ namespace safe::dsl::detail { if (overflow | underflow | lhs.is_overflow() | rhs.is_overflow()) { return checked{zero, true}; } else { - return checked{lhs.value() + rhs.value(), false}; + return checked(lhs.value() + rhs.value(), false); } } @@ -155,7 +155,7 @@ namespace safe::dsl::detail { if (overflow | underflow | lhs.is_overflow() | rhs.is_overflow()) { return checked{zero, true}; } else { - return checked{lhs.value() - rhs.value(), false}; + return checked(lhs.value() - rhs.value(), false); } } diff --git a/include/safe/dsl/detail/triint.hpp b/include/safe/dsl/detail/triint.hpp index 0116619..2ada257 100644 --- a/include/safe/dsl/detail/triint.hpp +++ b/include/safe/dsl/detail/triint.hpp @@ -1,6 +1,7 @@ #pragma once +#include #include #include diff --git a/include/safe/function.hpp b/include/safe/function.hpp new file mode 100644 index 0000000..c5af292 --- /dev/null +++ b/include/safe/function.hpp @@ -0,0 +1,95 @@ +#pragma once + + +#include +#include + +#include + +#include +#include + + +namespace safe { + /** + * @brief Create a safe, optionally piece-wise, function. + * + * If the arguments satisfy the first function's requirements, it is called + * and its return value is returned. If not, the second function is tried, + * and so on. If none of the arguments work for any of the functions' arguments, + * the last function is called with no arguments and its return value is + * returned. + * + * If RetT is void, then no default function should be specified and the + * function object's return value will be bool. + * + * @tparam RetT + * The return type of the function object. + * + * @param func + * The first function to attempt to apply to the arguments. + * + * @param remaining_funcs + * The remaining functions to attempt to apply to the arguments. + * + * @return A function object. + */ + template + [[nodiscard]] inline constexpr auto function( + auto func, + auto... remaining_funcs + ) { + using namespace boost::mp11; + + using func_arg_types = + detail::function_args_t; + + if constexpr (std::is_same_v) { + return [=](auto && ... args) -> bool { + static_assert( + mp_size::value == sizeof...(args), + "The number of arguments passed in must match the args in func."); + + bool const args_satisfy_reqs = + detail::check(func_arg_types{}, std::forward(args)...); + + if (args_satisfy_reqs) { + func(std::forward(args)...); + return true; + + } else if constexpr (sizeof...(remaining_funcs) > 0) { // check the remaining functions' requirements + return function(remaining_funcs...)(std::forward(args)...); + + } else { // no match, return failure + return false; + } + }; + + } else { + return [=](auto && ... args) -> RetT { + if constexpr (sizeof...(remaining_funcs) == 0) { + static_assert( + mp_size::value == 0, + "Last function in `safe::function` returns the default value and must not take any arguments."); + + return func(); + + } else { + static_assert( + mp_size::value == sizeof...(args), + "The number of arguments passed in must match the args in func."); + + bool const args_satisfy_reqs = + detail::check(func_arg_types{}, std::forward(args)...); + + if (args_satisfy_reqs) { + return func(std::forward(args)...); + + } else { // check the remaining functions' requirements + return function(remaining_funcs...)(std::forward(args)...); + } + } + }; + } + } +} \ No newline at end of file diff --git a/include/safe/int.hpp b/include/safe/int.hpp index cdec32d..76de0fb 100644 --- a/include/safe/int.hpp +++ b/include/safe/int.hpp @@ -89,7 +89,8 @@ namespace safe { typename T, char... Chars> [[nodiscard]] constexpr auto to_constant() { - constexpr T value = [](){ + // FIXME: handle or fail at compile-time for invalid strings + constexpr T value = [](){ constexpr std::array chars{Chars...}; T sum = 0; diff --git a/include/safe/invoke.hpp b/include/safe/invoke.hpp deleted file mode 100644 index 910980d..0000000 --- a/include/safe/invoke.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace safe { - template - constexpr bool invoke( - Callable c, - ArgTs... args - ) { - using var_list = detail::function_args_t; - using input_args = boost::mp11::mp_list; - - static_assert( - boost::mp11::mp_size::value == - boost::mp11::mp_size::value); - - bool const is_safe = - detail::check(var_list{}, args...); - - if (is_safe) { - c(args...); - return true; - } else { - return false; - } - } -} \ No newline at end of file diff --git a/include/safe/iostream.hpp b/include/safe/iostream.hpp new file mode 100644 index 0000000..cc14b1e --- /dev/null +++ b/include/safe/iostream.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include + +std::ostream& operator<<(std::ostream& os, safe::Var auto var) { + return os << var.unsafe_value(); +} \ No newline at end of file diff --git a/include/safe/var.hpp b/include/safe/var.hpp index c8e5305..7737cf7 100644 --- a/include/safe/var.hpp +++ b/include/safe/var.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include namespace safe { @@ -32,25 +34,33 @@ namespace safe { template friend inline constexpr auto detail::make_constant(); - friend constexpr auto value(auto value); + friend constexpr auto value(auto); template - friend constexpr bool invoke(Callable c, ArgTs... args); - - friend inline constexpr auto operator+(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator-(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator-(Var auto v); - friend inline constexpr auto operator*(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator/(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator%(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator<<(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator>>(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator|(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator&(Var auto lhs, Var auto rhs); - friend inline constexpr auto operator^(Var auto lhs, Var auto rhs); - friend inline constexpr auto min(Var auto lhs, Var auto rhs); - friend inline constexpr auto max(Var auto lhs, Var auto rhs); - friend inline constexpr auto abs(Var auto value); + friend constexpr bool invoke(Callable, ArgTs...); + + template + friend inline constexpr auto function(auto, auto...); + + template + friend inline constexpr auto accumulate(detail::iter_like auto, auto, auto); + + friend inline constexpr auto operator+(Var auto, Var auto); + friend inline constexpr auto operator-(Var auto, Var auto); + friend inline constexpr auto operator-(Var auto); + friend inline constexpr auto operator*(Var auto, Var auto); + friend inline constexpr auto operator/(Var auto, Var auto); + friend inline constexpr auto operator%(Var auto, Var auto); + friend inline constexpr auto operator<<(Var auto, Var auto); + friend inline constexpr auto operator>>(Var auto, Var auto); + friend inline constexpr auto operator|(Var auto, Var auto); + friend inline constexpr auto operator&(Var auto, Var auto); + friend inline constexpr auto operator^(Var auto, Var auto); + friend inline constexpr auto min(Var auto, Var auto); + friend inline constexpr auto max(Var auto, Var auto); + friend inline constexpr auto abs(Var auto); + + friend inline constexpr auto bit_width(Var auto); inline constexpr var(T value) : value_{value} @@ -166,15 +176,21 @@ namespace safe { return lhs.unsafe_value() == rhs.unsafe_value(); } - [[nodiscard]] inline constexpr auto abs( - Var auto value - ) { + [[nodiscard]] inline constexpr auto abs(Var auto value) { using num_t = decltype(value.unsafe_value()); auto result = static_cast(std::abs(value.unsafe_value())); auto result_req = dsl::detail::simp(safe::dsl::abs(value.requirement)); return var{result}; } + + [[nodiscard]] inline constexpr auto bit_width(Var auto value) { + using num_t = decltype(value.unsafe_value()); + auto result = static_cast(std::bit_width(value.unsafe_value())); + auto result_req = dsl::detail::simp(safe::dsl::bit_width(value.requirement)); + return var{result}; + } + [[nodiscard]] inline constexpr auto min( Var auto lhs, Var auto rhs diff --git a/test/safe/dsl/add.cpp b/test/safe/dsl/add.cpp index 363a25b..cd5851c 100644 --- a/test/safe/dsl/add.cpp +++ b/test/safe/dsl/add.cpp @@ -19,6 +19,13 @@ TEST(safe_dsl_add, add_two_ival_constants) { ); } +TEST(safe_dsl_add, add_three_ival_constants) { + EXPECT_EQ( + (ival<30, 30> + ival<12, 12> + ival<8, 8>), + (ival<50, 50>) + ); +} + TEST(safe_dsl_add, add_two_intervals) { EXPECT_EQ( (ival<10, 20> + ival<40, 80>), @@ -26,6 +33,20 @@ TEST(safe_dsl_add, add_two_intervals) { ); } +TEST(safe_dsl_add, add_three_intervals) { + EXPECT_EQ( + (ival<10, 20> + ival<40, 80> + ival<1, 5>), + (ival<51, 105>) + ); +} + +TEST(safe_dsl_add, add_four_intervals) { + EXPECT_EQ( + (ival<10, 20> + ival<40, 80> + ival<1, 5> + ival<3, 7>), + (ival<54, 112>) + ); +} + TEST(safe_dsl_add, add_two_mask_constants) { EXPECT_EQ( (mask<0, 12> + mask<0, 8>), diff --git a/test/safe/invoke.cpp b/test/safe/invoke.cpp index fc1ed59..96ad40f 100644 --- a/test/safe/invoke.cpp +++ b/test/safe/invoke.cpp @@ -40,35 +40,35 @@ void two_safe_vars( TEST_F(safe_invoke_test, simple_pass) { EXPECT_CALL(my_mock_function, two_safe_vars(2, 5)).Times(1); - bool const pass = safe::invoke(two_safe_vars, 2, 5); + bool const pass = safe::function(two_safe_vars)(2, 5); EXPECT_TRUE(pass); } TEST_F(safe_invoke_test, pass_with_an_input_var) { EXPECT_CALL(my_mock_function, two_safe_vars(9, 3)).Times(1); - bool const pass = safe::invoke(two_safe_vars, 9, 3_s32); + bool const pass = safe::function(two_safe_vars)(9, 3_s32); EXPECT_TRUE(pass); } TEST_F(safe_invoke_test, pass_with_both_input_vars) { EXPECT_CALL(my_mock_function, two_safe_vars(0, 4)).Times(1); - bool const pass = safe::invoke(two_safe_vars, 0_s32, 4_s32); + bool const pass = safe::function(two_safe_vars)(0_s32, 4_s32); EXPECT_TRUE(pass); } TEST_F(safe_invoke_test, simple_fail) { EXPECT_CALL(my_mock_function, two_safe_vars(_, _)).Times(0); - bool const fail = safe::invoke(two_safe_vars, 12, 5); + bool const fail = safe::function(two_safe_vars)(12, 5); EXPECT_FALSE(fail); } TEST_F(safe_invoke_test, fail_with_an_input_var) { EXPECT_CALL(my_mock_function, two_safe_vars(_, _)).Times(0); - bool const fail = safe::invoke(two_safe_vars, 11, 9_s32); + bool const fail = safe::function(two_safe_vars)(11, 9_s32); EXPECT_FALSE(fail); }