Skip to content

Commit

Permalink
Add BOOST_HANA_ENABLE_DEBUG_MODE and optional checks in make_set and …
Browse files Browse the repository at this point in the history
…make_map

Closes #230
  • Loading branch information
ldionne committed Jan 6, 2016
1 parent 2f38afd commit 7e04b36
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Expand Up @@ -97,6 +97,10 @@ matrix:
- env: UNIT_TESTS=true COMPILER=clang++-3.7 BOOST_VERSION=default CMAKE_OPTIONS="-DBOOST_HANA_ENABLE_CONCEPT_CHECKS=OFF"
addons: *clang37

# With debug mode
- env: UNIT_TESTS=true COMPILER=clang++-3.7 BOOST_VERSION=default CMAKE_OPTIONS="-DBOOST_HANA_ENABLE_DEBUG_MODE=ON"
addons: *clang37

# Without exceptions
- env: UNIT_TESTS=true COMPILER=clang++-3.7 BOOST_VERSION=default CMAKE_OPTIONS="-DBOOST_HANA_ENABLE_EXCEPTIONS=OFF"
addons: *clang37
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Expand Up @@ -20,6 +20,7 @@ include(CheckCxxCompilerSupport)
option(BOOST_HANA_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF)
option(BOOST_HANA_ENABLE_MEMCHECK "Run the unit tests and examples under Valgrind if it is found." OFF)
option(BOOST_HANA_ENABLE_CONCEPT_CHECKS "Enable concept checking in the interface methods." ON)
option(BOOST_HANA_ENABLE_DEBUG_MODE "Enable Hana's debug mode." OFF)

option(BOOST_HANA_ENABLE_STRING_UDL
"Enable the GNU extension allowing the special string literal operator\
Expand Down Expand Up @@ -52,6 +53,10 @@ if (NOT BOOST_HANA_ENABLE_CONCEPT_CHECKS)
add_definitions(-DBOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS)
endif()

if (BOOST_HANA_ENABLE_DEBUG_MODE)
add_definitions(-DBOOST_HANA_CONFIG_ENABLE_DEBUG_MODE)
endif()

if (BOOST_HANA_ENABLE_STRING_UDL)
add_definitions(-DBOOST_HANA_CONFIG_ENABLE_STRING_UDL)
# GCC pretends to have the flag, but produces a "unrecognized command line option"
Expand Down
13 changes: 13 additions & 0 deletions include/boost/hana/config.hpp
Expand Up @@ -197,4 +197,17 @@ Distributed under the Boost Software License, Version 1.0.
# define BOOST_HANA_CONFIG_ENABLE_STRING_UDL
#endif

#if defined(BOOST_HANA_DOXYGEN_INVOKED)
//! @ingroup group-config
//! Enables additional assertions and sanity checks to be done by Hana.
//!
//! When this macro is defined (it is __not defined__ by default),
//! additional sanity checks may be done by Hana. These checks may
//! be costly to perform, either in terms of compilation time or in
//! terms of execution time. These checks may help debugging an
//! application during its initial development, but they should not
//! be enabled as part of the normal configuration.
# define BOOST_HANA_CONFIG_ENABLE_DEBUG_MODE
#endif

#endif // !BOOST_HANA_CONFIG_HPP
65 changes: 65 additions & 0 deletions include/boost/hana/detail/has_duplicates.hpp
@@ -0,0 +1,65 @@
/*!
@file
Defines `boost::hana::detail::has_duplicates`.
@copyright Louis Dionne 2013-2016
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_HANA_DETAIL_HAS_DUPLICATES_HPP
#define BOOST_HANA_DETAIL_HAS_DUPLICATES_HPP

#include <boost/hana/config.hpp>
#include <boost/hana/detail/fast_and.hpp>
#include <boost/hana/equal.hpp>

#include <cstddef>
#include <utility>


BOOST_HANA_NAMESPACE_BEGIN namespace detail {
template <typename T, typename ...U>
constexpr std::size_t pack_count() {
std::size_t c = 0;
std::size_t expand[] = {0, // avoid empty array
(decltype(hana::equal(std::declval<T>(), std::declval<U>()))::value
? ++c
: c)...
};
(void)expand;

return c;
}

//! @ingroup group-details
//! Returns whether any of the `T`s are duplicate w.r.t. `hana::equal`.
//!
//! In particular, this does not check whether all of the `T`s are unique
//! as _types_, but rather whether they are unique when compared as
//! `hana::equal(std::declval<T>(), std::declval<U>())`. This assumes
//! the comparison to return an `IntegralConstant` that can be explicitly
//! converted to `bool`.
//!
//! @note
//! Since this utility is mostly used in assertions to check that there
//! are no duplicates in a sequence, we expect it to return `false` most
//! of the time (otherwise we will assert). Hence, this implementation is
//! biased towards the fact that we __will__ have to compare every pair of
//! elements in most cases, and it does not try to be lazy.
//!
//! @todo
//! This implementation is O(n^2). We could do it in O(n), but that would
//! require a more elaborate setup including storage with O(1) lookup
//! (which could be based on a compile-time hash). If we implement such
//! storage for associative sequences, we could use it to optimize this.
template <typename ...T>
struct has_duplicates {
static constexpr bool value =
sizeof...(T) > 0 &&
!detail::fast_and<(detail::pack_count<T, T...>() == 1)...>::value
;
};
} BOOST_HANA_NAMESPACE_END

#endif // !BOOST_HANA_DETAIL_HAS_DUPLICATES_HPP
8 changes: 6 additions & 2 deletions include/boost/hana/map.hpp
Expand Up @@ -26,6 +26,7 @@ Distributed under the Boost Software License, Version 1.0.
#include <boost/hana/core/make.hpp>
#include <boost/hana/detail/decay.hpp>
#include <boost/hana/detail/fast_and.hpp>
#include <boost/hana/detail/has_duplicates.hpp>
#include <boost/hana/detail/index_if.hpp>
#include <boost/hana/detail/operators/adl.hpp>
#include <boost/hana/detail/operators/comparable.hpp>
Expand Down Expand Up @@ -100,7 +101,7 @@ BOOST_HANA_NAMESPACE_BEGIN
struct make_impl<map_tag> {
template <typename ...Pairs>
static constexpr auto apply(Pairs&& ...pairs) {
#ifndef BOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS
#if defined(BOOST_HANA_CONFIG_ENABLE_DEBUG_MODE)
static_assert(detail::fast_and<hana::Product<Pairs>::value...>::value,
"hana::make_map(pairs...) requires all the 'pairs' to be Products");

Expand All @@ -116,7 +117,10 @@ BOOST_HANA_NAMESPACE_BEGIN
>::value,
"hana::make_map(pairs...) requires all the keys to be "
"Comparable at compile-time");
#endif

static_assert(!detail::has_duplicates<decltype(hana::first(pairs))...>::value,
"hana::make_map(pairs...) requires all the keys to be unique");
#endif

return map<typename detail::decay<Pairs>::type...>{
hana::make_tuple(static_cast<Pairs&&>(pairs)...)
Expand Down
8 changes: 6 additions & 2 deletions include/boost/hana/set.hpp
Expand Up @@ -22,6 +22,7 @@ Distributed under the Boost Software License, Version 1.0.
#include <boost/hana/core/make.hpp>
#include <boost/hana/detail/decay.hpp>
#include <boost/hana/detail/fast_and.hpp>
#include <boost/hana/detail/has_duplicates.hpp>
#include <boost/hana/detail/operators/adl.hpp>
#include <boost/hana/detail/operators/comparable.hpp>
#include <boost/hana/detail/operators/searchable.hpp>
Expand Down Expand Up @@ -92,7 +93,7 @@ BOOST_HANA_NAMESPACE_BEGIN
struct make_impl<set_tag> {
template <typename ...Xs>
static constexpr auto apply(Xs&& ...xs) {
#ifndef BOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS
#if defined(BOOST_HANA_CONFIG_ENABLE_DEBUG_MODE)
static_assert(detail::fast_and<hana::Comparable<Xs>::value...>::value,
"hana::make_set(xs...) requires all the 'xs' to be Comparable");

Expand All @@ -101,7 +102,10 @@ BOOST_HANA_NAMESPACE_BEGIN
>::value,
"hana::make_set(xs...) requires all the 'xs' to be "
"Comparable at compile-time");
#endif

static_assert(!detail::has_duplicates<Xs&&...>::value,
"hana::make_set(xs...) requires all the 'xs' to be unique");
#endif

return set<typename detail::decay<Xs>::type...>{
hana::make_tuple(static_cast<Xs&&>(xs)...)
Expand Down
59 changes: 59 additions & 0 deletions test/detail/has_duplicates.cpp
@@ -0,0 +1,59 @@
/*
@copyright Louis Dionne 2013-2016
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#include <boost/hana/detail/has_duplicates.hpp>
#include <boost/hana/integral_constant.hpp>
namespace hana = boost::hana;


static_assert(!hana::detail::has_duplicates<>::value, "");

static_assert(!hana::detail::has_duplicates<
hana::int_<0>
>::value, "");

static_assert(!hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>
>::value, "");

static_assert(!hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<2>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<0>, hana::int_<2>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<0>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<2>, hana::int_<1>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<2>, hana::int_<2>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<2>, hana::int_<1>, hana::int_<1>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<2>, hana::int_<1>, hana::int_<2>
>::value, "");

// Make sure it uses deep equality
static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::long_<0>, hana::int_<2>, hana::int_<3>
>::value, "");

static_assert(hana::detail::has_duplicates<
hana::int_<0>, hana::int_<1>, hana::int_<2>, hana::long_<1>
>::value, "");

int main() { }
12 changes: 10 additions & 2 deletions test/experimental/printable/set.cpp
Expand Up @@ -27,9 +27,17 @@ int main() {
{
std::ostringstream ss;
ss << hana::experimental::print(
hana::make_set(hana::int_c<1>, hana::char_c<'x'>, BOOST_HANA_STRING("3456"))
hana::make_set(hana::int_c<1>, BOOST_HANA_STRING("3456"))
);
BOOST_HANA_RUNTIME_CHECK(ss.str() == "{1, x, \"3456\"}");
BOOST_HANA_RUNTIME_CHECK(ss.str() == "{1, \"3456\"}");
}

{
std::ostringstream ss;
ss << hana::experimental::print(
hana::make_set(hana::char_c<'x'>, BOOST_HANA_STRING("3456"))
);
BOOST_HANA_RUNTIME_CHECK(ss.str() == "{x, \"3456\"}");
}

{
Expand Down

0 comments on commit 7e04b36

Please sign in to comment.