From 02f4dd1793f0f9221f07ba777d4e5518833fdcc1 Mon Sep 17 00:00:00 2001 From: rbock Date: Thu, 24 Dec 2015 21:11:00 +0100 Subject: [PATCH] Migrated case to portable static assert and added static tests --- include/sqlpp11/case.h | 54 ++++---- include/sqlpp11/consistent.h | 2 +- include/sqlpp11/insert_value_list.h | 4 +- include/sqlpp11/portable_static_assert.h | 3 +- include/sqlpp11/where.h | 4 +- test_static_asserts/CMakeLists.txt | 1 + test_static_asserts/case.cpp | 160 +++++++++++++++++++++++ test_static_asserts/where.cpp | 31 +++-- tests/MockDb.h | 9 +- 9 files changed, 219 insertions(+), 49 deletions(-) create mode 100644 test_static_asserts/case.cpp diff --git a/include/sqlpp11/case.h b/include/sqlpp11/case.h index 5183fd8b..99a22e4b 100644 --- a/include/sqlpp11/case.h +++ b/include/sqlpp11/case.h @@ -34,22 +34,29 @@ namespace sqlpp { - namespace detail - { - template - using valid_when_t = - logic::all_t>::value, is_expression_t>::value>; + SQLPP_PORTABLE_STATIC_ASSERT(assert_case_else_expression_t, "argument is not a value expression in else()"); + SQLPP_PORTABLE_STATIC_ASSERT(assert_case_then_else_same_type_t, + "argument of then() and else() are not of the same type"); - template - using valid_then_t = is_expression_t>; + template + using check_case_else_t = static_combined_check_t< + static_check_t>::value, assert_case_else_expression_t>, + static_check_t::value, + is_sql_null_t>::value, + std::is_same, value_type_of>>::value>::value, + assert_case_then_else_same_type_t>>; - template - using valid_else_t = logic::all_t< - is_expression_t>::value, - logic::any_t::value, - is_sql_null_t>::value, - std::is_same, value_type_of>>::value>::value>; - } + SQLPP_PORTABLE_STATIC_ASSERT(assert_case_then_expression_t, "argument is not a value expression in then()"); + template + using check_case_then_t = + static_check_t>::value>::value, assert_case_then_expression_t>; + + SQLPP_PORTABLE_STATIC_ASSERT(assert_case_when_boolean_expression_t, + "argument is not a boolean expression in case_when()"); + template + using check_case_when_t = static_check_t< + logic::all_t>::value, is_expression_t>::value>::value, + assert_case_when_boolean_expression_t>; template struct case_t @@ -100,11 +107,10 @@ namespace sqlpp ~case_then_t() = default; template - auto else_(Else else_) -> decltype(this->_else_impl(detail::valid_else_t{}, else_)) + auto else_(Else else_) -> decltype(this->_else_impl(check_case_else_t{}, else_)) { - static_assert(detail::valid_else_t::value, - "arguments of then and else must be expressions of the same type (or null)"); - return _else_impl(detail::valid_else_t{}, else_); + check_case_else_t::_(); + return _else_impl(check_case_else_t{}, else_); } private: @@ -136,10 +142,10 @@ namespace sqlpp ~case_when_t() = default; template - auto then(Then t) -> decltype(this->_then_impl(detail::valid_then_t{}, t)) + auto then(Then t) -> decltype(this->_then_impl(check_case_then_t{}, t)) { - static_assert(detail::valid_then_t::value, "then argument must be a value expression"); - return _then_impl(detail::valid_then_t{}, t); + check_case_then_t::_(); + return _then_impl(check_case_then_t{}, t); } private: @@ -178,11 +184,11 @@ namespace sqlpp } template - auto case_when(When when) -> decltype(detail::case_when_impl(detail::valid_when_t{}, when)) + auto case_when(When when) -> decltype(detail::case_when_impl(check_case_when_t{}, when)) { - static_assert(detail::valid_when_t::value, "case_when condition must be a boolean expression"); + check_case_when_t::_(); - return detail::case_when_impl(detail::valid_when_t{}, when); + return detail::case_when_impl(typename check_case_when_t::type{}, when); } } diff --git a/include/sqlpp11/consistent.h b/include/sqlpp11/consistent.h index 7f6e765c..6baa2452 100644 --- a/include/sqlpp11/consistent.h +++ b/include/sqlpp11/consistent.h @@ -31,7 +31,7 @@ namespace sqlpp { - struct consistent_t + struct consistent_t : std::true_type { static constexpr bool value = true; using type = std::true_type; diff --git a/include/sqlpp11/insert_value_list.h b/include/sqlpp11/insert_value_list.h index 059356bb..e1322c05 100644 --- a/include/sqlpp11/insert_value_list.h +++ b/include/sqlpp11/insert_value_list.h @@ -438,7 +438,7 @@ namespace sqlpp using Check = check_insert_static_set_t; Check{}._(); - return _set_impl(typename Check::type{}, assignments...); + return _set_impl(Check{}, assignments...); } template @@ -449,7 +449,7 @@ namespace sqlpp using Check = check_insert_dynamic_set_t<_database_t, Assignments...>; Check{}._(); - return _set_impl<_database_t>(typename Check::type{}, assignments...); + return _set_impl<_database_t>(Check{}, assignments...); } private: diff --git a/include/sqlpp11/portable_static_assert.h b/include/sqlpp11/portable_static_assert.h index 9246be9b..6574fc56 100644 --- a/include/sqlpp11/portable_static_assert.h +++ b/include/sqlpp11/portable_static_assert.h @@ -30,9 +30,8 @@ namespace sqlpp { #define SQLPP_PORTABLE_STATIC_ASSERT(name, message) \ - struct name \ + struct name : std::false_type \ { \ - static constexpr bool value = false; \ using type = std::false_type; \ \ template \ diff --git a/include/sqlpp11/where.h b/include/sqlpp11/where.h index 3a452aa0..cc1dbbab 100644 --- a/include/sqlpp11/where.h +++ b/include/sqlpp11/where.h @@ -296,7 +296,7 @@ namespace sqlpp using Check = check_where_static_t; Check{}._(); - return _where_impl(typename Check::type{}, expressions...); + return _where_impl(Check{}, expressions...); } template @@ -306,7 +306,7 @@ namespace sqlpp using Check = check_where_dynamic_t<_database_t, Expressions...>; Check{}._(); - return _where_impl<_database_t>(typename Check::type{}, expressions...); + return _where_impl<_database_t>(Check{}, expressions...); } private: diff --git a/test_static_asserts/CMakeLists.txt b/test_static_asserts/CMakeLists.txt index a4d532ad..54d9b158 100644 --- a/test_static_asserts/CMakeLists.txt +++ b/test_static_asserts/CMakeLists.txt @@ -29,6 +29,7 @@ function(test_compile name) endfunction() test_compile(aggregates) +test_compile(case) test_compile(where) test_compile(insert) test_compile(date) diff --git a/test_static_asserts/case.cpp b/test_static_asserts/case.cpp new file mode 100644 index 00000000..bfea0b2f --- /dev/null +++ b/test_static_asserts/case.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015-2015, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "MockDb.h" +#include "Sample.h" +#include + +namespace +{ + constexpr auto t = test::TabBar{}; + constexpr auto f = test::TabFoo{}; + + template + void print_type_on_error(std::true_type) + { + } + + template + void print_type_on_error(std::false_type) + { + T::_print_me_; + } + + template + void when_check(const When& when) + { + using CheckResult = sqlpp::check_case_when_t; + using ExpectedCheckResult = std::is_same; + print_type_on_error(ExpectedCheckResult{}); + static_assert(ExpectedCheckResult::value, "Unexpected check result"); + + using ReturnType = decltype(sqlpp::case_when(when)); + using ExpectedReturnType = sqlpp::logic::all_t::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + template + void then_check(const When& when, const Then& then) + { + using CheckResult = sqlpp::check_case_then_t; + using ExpectedCheckResult = std::is_same; + print_type_on_error(ExpectedCheckResult{}); + static_assert(ExpectedCheckResult::value, "Unexpected check result"); + + using ReturnType = decltype(sqlpp::case_when(when).then(then)); + using ExpectedReturnType = sqlpp::logic::all_t::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + template + void else_check(const When& when, const Then& then, const Else& else_) + { + using CheckResult = sqlpp::check_case_else_t, Else>; + using ExpectedCheckResult = std::is_same; + print_type_on_error(ExpectedCheckResult{}); + static_assert(ExpectedCheckResult::value, "Unexpected check result"); + + using ReturnType = decltype(sqlpp::case_when(when).then(then).else_(else_)); + using ExpectedReturnType = sqlpp::logic::all_t::value>; + print_type_on_error(ExpectedReturnType{}); + static_assert(ExpectedReturnType::value, "Unexpected return type"); + } + + void when() + { + // OK + when_check(t.gamma); + when_check(t.gamma == true); + when_check(count(t.alpha) > 0); + + // Try assignment as "when" + when_check(t.gamma = true); + + // Try non-boolean expression as "when" + when_check(t.alpha); + + // Try some other types as "when" + when_check("true"); + when_check(42); + when_check('c'); + when_check(nullptr); + + // Try to use a table as "when" + when_check(t); + + // Try to use an alias as "when" + when_check(t.gamma.as(t.beta)); + } + + void then() + { + // OK + then_check(t.gamma, t.gamma); + then_check(t.gamma, t.gamma == true); + then_check(t.gamma, count(t.alpha) > 0); + then_check(t.gamma, t.alpha); + then_check(t.gamma, "true"); + then_check(t.gamma, 42); + then_check(t.gamma, 'c'); + then_check(t.gamma, nullptr); + + // Try to use an assignment as "then" + then_check(t.gamma, t.gamma = true); + + // Try to use a table as "then" + then_check(t.gamma, t); + + // Try to use an alias as "then" + then_check(t.gamma, t.alpha.as(t.beta)); + } + + void else_() + { + // OK + else_check(t.gamma, t.gamma, t.gamma); + else_check(t.gamma, t.alpha, 42); + else_check(t.gamma, t.beta, "twentyseven"); + + // Try to use an assignment as "else" + else_check(t.gamma, t.alpha, t.alpha = 7); + + // Try to use a table as "else" + else_check(t.gamma, t.alpha, t); + + // Try to use an alias as "else" + else_check(t.gamma, t.alpha, t.alpha.as(t.beta)); + } +} + +int main(int, char**) +{ + when(); + then(); + else_(); +} diff --git a/test_static_asserts/where.cpp b/test_static_asserts/where.cpp index 5d300818..b7cdb0bd 100644 --- a/test_static_asserts/where.cpp +++ b/test_static_asserts/where.cpp @@ -60,7 +60,7 @@ namespace } template - void set_dynamic_check(const Expressions&... expressions) + void where_dynamic_check(const Expressions&... expressions) { static auto db = MockDb{}; using CheckResult = sqlpp::check_where_dynamic_t; @@ -95,35 +95,40 @@ namespace where_static_check(17); where_static_check('c'); where_static_check(nullptr); + where_static_check(t.alpha.as(t.beta)); // Try using aggregate functions in where where_static_check(count(t.alpha) > 0); where_static_check(t.gamma and count(t.alpha) > 0); + where_static_check( + case_when(count(t.alpha) > 0).then(t.gamma).else_(not t.gamma)); } - // column alpha is not allowed, column gamma is required void dynamic_where() { // OK - set_dynamic_check(); - where_static_check(t.gamma); - where_static_check(t.gamma == true); + where_dynamic_check(); + where_dynamic_check(t.gamma); + where_dynamic_check(t.gamma == true); // Try assignment as condition - where_static_check(t.gamma = true); + where_dynamic_check(t.gamma = true); // Try non-boolean expression - where_static_check(t.alpha); + where_dynamic_check(t.alpha); // Try some other types as expressions - where_static_check("true"); - where_static_check(17); - where_static_check('c'); - where_static_check(nullptr); + where_dynamic_check("true"); + where_dynamic_check(17); + where_dynamic_check('c'); + where_dynamic_check(nullptr); + where_dynamic_check(t.alpha.as(t.beta)); // Try using aggregate functions in where - where_static_check(count(t.alpha) > 0); - where_static_check(t.gamma and count(t.alpha) > 0); + where_dynamic_check(count(t.alpha) > 0); + where_dynamic_check(t.gamma and count(t.alpha) > 0); + where_dynamic_check( + case_when(count(t.alpha) > 0).then(t.gamma).else_(not t.gamma)); // Try dynamic_where on a non-dynamic remove using CheckResult = sqlpp::check_where_dynamic_t; diff --git a/tests/MockDb.h b/tests/MockDb.h index a90d4eae..de055377 100644 --- a/tests/MockDb.h +++ b/tests/MockDb.h @@ -120,10 +120,10 @@ struct MockDbT : public sqlpp::connection auto _run(const T& t, const std::false_type&) -> void; template - auto operator()(const T& t) -> decltype(this->_run(t, typename sqlpp::run_check_t<_serializer_context_t, T>::type{})) + auto operator()(const T& t) -> decltype(this->_run(t, sqlpp::run_check_t<_serializer_context_t, T>{})) { sqlpp::run_check_t<_serializer_context_t, T>::_(); - return _run(t, typename sqlpp::run_check_t<_serializer_context_t, T>::type{}); + return _run(t, sqlpp::run_check_t<_serializer_context_t, T>{}); } size_t execute(const std::string&) @@ -191,11 +191,10 @@ struct MockDbT : public sqlpp::connection auto _prepare(const T& t, const std::false_type&) -> void; template - auto prepare(const T& t) - -> decltype(this->_prepare(t, typename sqlpp::prepare_check_t<_serializer_context_t, T>::type{})) + auto prepare(const T& t) -> decltype(this->_prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{})) { sqlpp::prepare_check_t<_serializer_context_t, T>::_(); - return _prepare(t, typename sqlpp::prepare_check_t<_serializer_context_t, T>::type{}); + return _prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{}); } template