diff --git a/example/string.cpp b/example/string.cpp new file mode 100644 index 000000000..da62c8cd9 --- /dev/null +++ b/example/string.cpp @@ -0,0 +1,108 @@ +/* +@copyright Louis Dionne 2014 +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 +#include +#include +#include +#include +#include +using namespace boost::hana; + + +int main() { + +{ + +//! [BOOST_HANA_STRING] +BOOST_HANA_CONSTEXPR_LAMBDA auto str = BOOST_HANA_STRING("abcdef"); +BOOST_HANA_CONSTANT_CHECK(is_a(str)); +//! [BOOST_HANA_STRING] + +}{ + +//! [string] +constexpr auto str = string<'a', 'b', 'c', 'd', 'e', 'f'>; +BOOST_HANA_CONSTANT_CHECK(is_a(str)); +//! [string] + +}{ + +//! [comparable] +BOOST_HANA_CONSTANT_CHECK( + BOOST_HANA_STRING("abcdef") == BOOST_HANA_STRING("abcdef") +); + +BOOST_HANA_CONSTANT_CHECK( + BOOST_HANA_STRING("abcdef") != BOOST_HANA_STRING("abef") +); +//! [comparable] + +}{ + +//! [orderable] +BOOST_HANA_CONSTANT_CHECK( + BOOST_HANA_STRING("abc") < BOOST_HANA_STRING("bcd") +); + +BOOST_HANA_CONSTANT_CHECK( + BOOST_HANA_STRING("abcd") > BOOST_HANA_STRING("abc") +); +//! [orderable] + +}{ + +//! [constant] +/* not constexpr */ auto abcdef = BOOST_HANA_STRING("abcdef"); +constexpr char const* s = value(abcdef); +static_assert(s[2] == 'c', ""); +//! [constant] + +}{ + +//! [foldable] +auto sum_string = [](auto str) { + return foldl(str, int_<0>, [](auto sum, auto c) { + constexpr int i = value(c) - 48; // convert character to decimal + return sum + int_; + }); +}; + +BOOST_HANA_CONSTANT_CHECK( + sum_string(BOOST_HANA_STRING("1234")) == int_<1 + 2 + 3 + 4> +); +//! [foldable] + +}{ + +//! [iterable] +BOOST_HANA_CONSTANT_CHECK(!is_empty(BOOST_HANA_STRING("abcd"))); +BOOST_HANA_CONSTANT_CHECK(is_empty(BOOST_HANA_STRING(""))); + +BOOST_HANA_CONSTANT_CHECK(BOOST_HANA_STRING("abcd")[int_<2>] == char_<'c'>); + +auto is_vowel = [](auto c) { + return c ^in^ BOOST_HANA_STRING("aeiouy"); +}; +BOOST_HANA_CONSTANT_CHECK( + drop_while(BOOST_HANA_STRING("aioubcd"), is_vowel) == BOOST_HANA_STRING("bcd") +); +//! [iterable] + +}{ + +//! [searchable] +BOOST_HANA_CONSTANT_CHECK(char_<'c'> ^in^ BOOST_HANA_STRING("abcde")); +BOOST_HANA_CONSTANT_CHECK(!(char_<'z'> ^in^ BOOST_HANA_STRING("abcde"))); + +BOOST_HANA_CONSTANT_CHECK( + lookup(BOOST_HANA_STRING("abcxefg"), char_<'x'>) == just(char_<'x'>) +); +//! [searchable] + +} + +} diff --git a/example/string/comparable.cpp b/example/string/comparable.cpp deleted file mode 100644 index cb56e43ab..000000000 --- a/example/string/comparable.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -#include -using namespace boost::hana; - - -int main() { - //! [main] - BOOST_HANA_CONSTANT_CHECK( - BOOST_HANA_STRING("abcdef") == BOOST_HANA_STRING("abcdef") - ); - - BOOST_HANA_CONSTANT_CHECK( - BOOST_HANA_STRING("abcdef") != BOOST_HANA_STRING("abef") - ); - //! [main] -} diff --git a/example/string/constant.cpp b/example/string/constant.cpp deleted file mode 100644 index 96ca171c9..000000000 --- a/example/string/constant.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -using namespace boost::hana; - - -int main() { - //! [main] - /* not constexpr */ auto abcdef = BOOST_HANA_STRING("abcdef"); - constexpr char const* s = value(abcdef); - //! [main] - - (void)s; -} diff --git a/example/string/foldable.cpp b/example/string/foldable.cpp deleted file mode 100644 index d96039882..000000000 --- a/example/string/foldable.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -#include -#include -using namespace boost::hana; - - -int main() { - //! [main] - auto sum_string = [](auto str) { - return foldl(str, int_<0>, [](auto sum, auto c) { - constexpr int i = value(c) - 48; // convert character to decimal - return sum + int_; - }); - }; - - BOOST_HANA_CONSTANT_CHECK( - sum_string(BOOST_HANA_STRING("1234")) == int_<1 + 2 + 3 + 4> - ); - //! [main] -} diff --git a/example/string/iterable.cpp b/example/string/iterable.cpp deleted file mode 100644 index 8893ae288..000000000 --- a/example/string/iterable.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -#include -#include -using namespace boost::hana; - - -int main() { - //! [main] - BOOST_HANA_CONSTANT_CHECK(!is_empty(BOOST_HANA_STRING("abcd"))); - BOOST_HANA_CONSTANT_CHECK(is_empty(BOOST_HANA_STRING(""))); - - BOOST_HANA_CONSTANT_CHECK(BOOST_HANA_STRING("abcd")[int_<2>] == char_<'c'>); - - auto is_vowel = [](auto c) { - return c ^in^ BOOST_HANA_STRING("aeiouy"); - }; - BOOST_HANA_CONSTANT_CHECK( - drop_while(BOOST_HANA_STRING("aioubcd"), is_vowel) == BOOST_HANA_STRING("bcd") - ); - //! [main] -} diff --git a/example/string/orderable.cpp b/example/string/orderable.cpp deleted file mode 100644 index a39a2fb18..000000000 --- a/example/string/orderable.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -#include -using namespace boost::hana; - - -int main() { - //! [main] - BOOST_HANA_CONSTANT_CHECK( - BOOST_HANA_STRING("abc") < BOOST_HANA_STRING("bcd") - ); - - BOOST_HANA_CONSTANT_CHECK( - BOOST_HANA_STRING("abcd") > BOOST_HANA_STRING("abc") - ); - //! [main] -} diff --git a/example/string/searchable.cpp b/example/string/searchable.cpp deleted file mode 100644 index ea6af3d7d..000000000 --- a/example/string/searchable.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -#include -#include -#include -using namespace boost::hana; - - -int main() { - //! [main] - BOOST_HANA_CONSTANT_CHECK(char_<'c'> ^in^ BOOST_HANA_STRING("abcde")); - BOOST_HANA_CONSTANT_CHECK(!(char_<'z'> ^in^ BOOST_HANA_STRING("abcde"))); - - BOOST_HANA_CONSTANT_CHECK( - lookup(BOOST_HANA_STRING("abcxefg"), char_<'x'>) == just(char_<'x'>) - ); - //! [main] -} diff --git a/example/string/string.cpp b/example/string/string.cpp deleted file mode 100644 index e9a647209..000000000 --- a/example/string/string.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* -@copyright Louis Dionne 2014 -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 -#include -using namespace boost::hana; - - -int main() { - { - //! [macro] - BOOST_HANA_CONSTEXPR_LAMBDA auto s = BOOST_HANA_STRING("abcdef"); - //! [macro] - (void)s; - } -} diff --git a/include/boost/hana/fwd/string.hpp b/include/boost/hana/fwd/string.hpp index d9e98f0c9..707f7cf72 100644 --- a/include/boost/hana/fwd/string.hpp +++ b/include/boost/hana/fwd/string.hpp @@ -10,21 +10,46 @@ Distributed under the Boost Software License, Version 1.0. #ifndef BOOST_HANA_FWD_STRING_HPP #define BOOST_HANA_FWD_STRING_HPP -#include -#include -#include -#include -#include -#include - - namespace boost { namespace hana { //! @ingroup group-datatypes //! Represents a compile-time string. //! - //! ## Instance of - //! `Comparable`, `Orderable`, `Constant`, `Foldable`, `Iterable`, and - //! `Searchable` + //! + //! Modeled concepts + //! ---------------- + //! For most purposes, a `String` is functionally equivalent to a tuple + //! holding `IntegralConstant`s of underlying type `char`. + //! + //! 1. `Comparable` (operators provided)\n + //! Two `String`s are equal if and only if they have the same number of + //! characters and characters at corresponding indices are equal. + //! @snippet example/string.cpp comparable + //! + //! 2. `Orderable` (operators provided)\n + //! The strict weak ordering implemented for `Orderable` is the usual + //! lexicographical comparison of strings. + //! @snippet example/string.cpp orderable + //! + //! 3. `Constant`\n + //! A `String`'s compile-time value is a constexpr `char const*` to its + //! internal data. + //! @snippet example/string.cpp constant + //! + //! 4. `Foldable`\n + //! Folding a `String` is equivalent to folding the sequence of its + //! characters. + //! @snippet example/string.cpp foldable + //! + //! 5. `Iterable` (operators provided)\n + //! Iterating over a `String` is equivalent to iterating over the sequence + //! of its characters. + //! @snippet example/string.cpp iterable + //! + //! 6. `Searchable`\n + //! Searching through a `String` is equivalent to searching through the + //! sequence of its characters. + //! @snippet example/string.cpp searchable + //! //! //! @todo //! Right now, we use a different template specialization to store each @@ -33,47 +58,30 @@ namespace boost { namespace hana { //! in some circumstances: http://llvm.org/bugs/show_bug.cgi?id=20625. //! Using an anonymous type could have compile-time performance benefits, //! so this avenue should be explored once the bug is fixed. - struct String { - struct hana { - struct enabled_operators - : Comparable - , Orderable - , Iterable - { }; - }; - }; + //! + //! @todo + //! The `BOOST_HANA_STRING` macro does not appear near `String` in the + //! documentation. + struct String { using value_type = char const*; }; //! Create a compile-time string from a parameter pack of characters. //! @relates String + //! + //! + //! Example + //! ------- + //! @snippet example/string.cpp string #ifdef BOOST_HANA_DOXYGEN_INVOKED template constexpr unspecified-type string{}; #else template - struct _string - : operators::enable_adl - , operators::Iterable_ops<_string> - { - struct hana { using datatype = String; }; - }; + struct _string; template constexpr _string string{}; #endif - namespace string_detail { - template - constexpr decltype(auto) - prepare_impl(S, detail::std::index_sequence) - { return string; } - - template - constexpr decltype(auto) prepare(S s) { - return prepare_impl(s, - detail::std::make_index_sequence{}); - } - } - //! Create a compile-time string from a string literal `s`. //! @relates String //! @@ -81,20 +89,20 @@ namespace boost { namespace hana { //! compile-time strings. However, since this macro uses a lambda //! internally, it can't be used in an unevaluated context. //! - //! ### Example - //! @snippet example/string/string.cpp macro + //! Example + //! ------- + //! @snippet example/string.cpp BOOST_HANA_STRING //! //! @todo - //! This could be generalized to arbitrary objects. This is basically a + //! - This could be generalized to arbitrary objects. This is basically a //! way to create a `Constant` from any `constexpr` object. -#define BOOST_HANA_STRING(s) \ - (::boost::hana::string_detail::prepare([]{ \ - struct tmp { \ - static constexpr decltype(auto) storage() { return s; } \ - }; \ - return tmp{}; \ - }())) \ -/**/ + //! - Use the generic conversion from any `Constant` to implement this + //! instead of the custom `prepare` function. +#ifdef BOOST_HANA_DOXYGEN_INVOKED +# define BOOST_HANA_STRING(s) unspecified +#else + // defined in boost/hana/string.hpp +#endif }} // end namespace boost::hana #endif // !BOOST_HANA_FWD_STRING_HPP diff --git a/include/boost/hana/iterable.hpp b/include/boost/hana/iterable.hpp index 305a6645d..584f4c02c 100644 --- a/include/boost/hana/iterable.hpp +++ b/include/boost/hana/iterable.hpp @@ -194,7 +194,7 @@ namespace boost { namespace hana { //! ### Example 2 //! @snippet example/integer_list/foldable.cpp foldr template - struct Foldable::instance()>> + struct Foldable::instance() && !models{}>> : Foldable::iterable_mcd { }; @@ -252,7 +252,7 @@ namespace boost { namespace hana { //! ### Example //! @snippet example/iterable.cpp find template - struct Searchable::instance()>> + struct Searchable::instance() && !models{}>> : Searchable::iterable_mcd { }; diff --git a/include/boost/hana/string.hpp b/include/boost/hana/string.hpp index c8de631ef..64726bda0 100644 --- a/include/boost/hana/string.hpp +++ b/include/boost/hana/string.hpp @@ -13,121 +13,105 @@ Distributed under the Boost Software License, Version 1.0. #include #include -#include -#include -#include - -//! @todo -//! This enables conversion from any Foldable to a List, so String -> List -//! works. -#include - -// instances #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include +#include #include #include namespace boost { namespace hana { - //! Instance of `Comparable` for `String`s. - //! - //! Two `String`s are equal if and only if each of their - //! characters are equal. - //! - //! ### Example - //! @snippet example/string/comparable.cpp main - template <> - struct Comparable::instance : Comparable::equal_mcd { + ////////////////////////////////////////////////////////////////////////// + // string + ////////////////////////////////////////////////////////////////////////// + template + struct _string + : operators::enable_adl + , operators::Iterable_ops<_string> + { }; + + template + struct datatype<_string> { + using type = String; + }; + + ////////////////////////////////////////////////////////////////////////// + // BOOST_HANA_STRING + ////////////////////////////////////////////////////////////////////////// + namespace string_detail { + template + constexpr decltype(auto) + prepare_impl(S, detail::std::index_sequence) + { return string; } + template - static constexpr auto equal_impl(S const&, S const&) + constexpr decltype(auto) prepare(S s) { + return prepare_impl(s, + detail::std::make_index_sequence{}); + } + } + +#define BOOST_HANA_STRING(s) \ + (::boost::hana::string_detail::prepare([]{ \ + struct tmp { \ + static constexpr decltype(auto) get() { return s; } \ + }; \ + return tmp{}; \ + }())) \ +/**/ + + ////////////////////////////////////////////////////////////////////////// + // Operators + ////////////////////////////////////////////////////////////////////////// + template <> + struct enabled_operators + : Comparable, Orderable, Iterable + { }; + + ////////////////////////////////////////////////////////////////////////// + // Comparable + ////////////////////////////////////////////////////////////////////////// + template <> + struct models + : detail::std::true_type + { }; + + template <> + struct equal_impl { + template + static constexpr auto apply(S const&, S const&) { return true_; } template - static constexpr auto equal_impl(S1 const&, S2 const&) + static constexpr auto apply(S1 const&, S2 const&) { return false_; } }; - namespace string_detail { - template - constexpr char const store[sizeof...(s) + 1] = {s..., '\0'}; - } - - //! Instance of `Constant` for `String`s. - //! - //! A `String`'s compile-time value is a constexpr `char const*` to its - //! internal data. - //! - //! ### Example - //! @snippet example/string/constant.cpp main + ////////////////////////////////////////////////////////////////////////// + // Orderable + ////////////////////////////////////////////////////////////////////////// template <> - struct Constant::instance : Constant::mcd { - template - static constexpr char const* value_impl(_string const&) - { return string_detail::store; } - }; + struct models + : detail::std::true_type + { }; - //! Instance of `Foldable` for `String`s. - //! - //! For the purpose of being folded, `String`s are basically equivalent to - //! a tuple in which `IntegralConstant`s of underlying type `char` are - //! stored. - //! - //! ### Example - //! @snippet example/string/foldable.cpp main template <> - struct Foldable::instance : Foldable::unpack_mcd { - template - static constexpr decltype(auto) - unpack_impl(_string const&, F&& f) - { return detail::std::forward(f)(char_...); } - - template - static constexpr auto length_impl(_string const&) - { return size_t; } - }; - - //! Instance of `Iterable` for `String`s. - //! - //! For the purpose of being iterated over, `String`s are basically - //! equivalent to a tuple of `IntegralConstant`s with an underlying - //! type of `char`. - //! - //! ### Example - //! @snippet example/string/iterable.cpp main - template <> - struct Iterable::instance : Iterable::mcd { - template - static constexpr auto head_impl(_string const&) - { return char_; } - - template - static constexpr auto tail_impl(_string const&) - { return string; } - - template - static constexpr auto is_empty_impl(_string const&) - { return bool_; } - - template - static constexpr auto at_impl(I index, _string const&) { - constexpr char characters[] = {s...}; - constexpr auto i = value(index); - return char_; - } - }; - - //! Instance of `Orderable` for `String`s. - //! - //! The strict weak ordering implemented for `Orderable` is the usual - //! lexicographical comparison. - //! - //! ### Example - //! @snippet example/string/orderable.cpp main - template <> - struct Orderable::instance : Orderable::less_mcd { + struct less_impl { static constexpr bool less_helper(char const* s1, char const* s2) { while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2) ++s1, ++s2; @@ -138,23 +122,171 @@ namespace boost { namespace hana { template static constexpr auto - less_impl(_string const&, _string const&) { + apply(_string const&, _string const&) { constexpr char const c_str1[] = {s1..., '\0'}; constexpr char const c_str2[] = {s2..., '\0'}; return bool_; } }; - //! Instance of `Searchable` for `String`s. - //! - //! For the purpose of being searched, `String`s are basically equivalent - //! to a tuple of `IntegralConstant`s of underlying type `char`. - //! - //! ### Example - //! @snippet example/string/searchable.cpp main + ////////////////////////////////////////////////////////////////////////// + // Constant + ////////////////////////////////////////////////////////////////////////// template <> - struct Searchable::instance - : Searchable::iterable_mcd + struct models + : detail::std::true_type + { }; + + namespace string_detail { + template + constexpr char const store[sizeof...(s) + 1] = {s..., '\0'}; + } + + template <> + struct value_impl { + template + static constexpr char const* apply(_string const&) + { return string_detail::store; } + }; + + template + struct to_impl{} && + detail::std::is_same{} + >> : embedding<> { + template + static constexpr auto helper(S s, detail::std::index_sequence) + { return string; } + + static constexpr detail::std::size_t strlen(char const* s) { + detail::std::size_t len = 0; + while (*s++ != '\0') ++len; + return len; + } + + template + static constexpr decltype(auto) apply(S s) { + constexpr detail::std::size_t len = strlen(hana::value(s)); + return helper(s, detail::std::make_index_sequence{}); + } + }; + + ////////////////////////////////////////////////////////////////////////// + // Foldable + ////////////////////////////////////////////////////////////////////////// + template <> + struct models + : detail::std::true_type + { }; + + template <> + struct unpack_impl { + template + static constexpr decltype(auto) apply(_string const&, F&& f) + { return detail::std::forward(f)(char_...); } + }; + + template <> + struct length_impl { + template + static constexpr auto apply(_string const&) + { return size_t; } + }; + + ////////////////////////////////////////////////////////////////////////// + // Iterable + ////////////////////////////////////////////////////////////////////////// + template <> + struct models + : detail::std::true_type + { }; + + template <> + struct head_impl { + template + static constexpr auto apply(_string const&) + { return char_; } + }; + + template <> + struct tail_impl { + template + static constexpr auto apply(_string const&) + { return string; } + }; + + template <> + struct is_empty_impl { + template + static constexpr auto apply(_string const&) + { return bool_; } + }; + + template <> + struct at_impl { + template + static constexpr auto apply(I index, _string const&) { + constexpr char characters[] = {s...}; + constexpr auto i = hana::value(index); + return char_; + } + }; + + ////////////////////////////////////////////////////////////////////////// + // Searchable + ////////////////////////////////////////////////////////////////////////// + template <> + struct models + : detail::std::true_type + { }; + + template <> + struct find_impl + : Iterable::find_impl + { }; + + template <> + struct elem_impl { + static constexpr bool str_elem(char const* s, char c) { + while (*s != '\0') + if (*s++ == c) + return true; + return false; + } + + template + static constexpr auto + helper(_string, Char c, detail::std::true_type) { + constexpr char c_str[] = {s..., '\0'}; + return bool_; + } + + template + static constexpr auto helper(S, Char, detail::std::false_type) + { return false_; } + + template + static constexpr decltype(auto) apply(S s, Char c) { + return helper(s, c, detail::std::integral_constant(c) + >{}); + } + }; + + template <> + struct lookup_impl { + template + static constexpr auto apply(_string str, Char c) { + return hana::if_(hana::elem(str, c), + hana::just(c), + nothing + ); + } + }; + + template <> + struct any_impl + : Iterable::any_impl { }; }} // end namespace boost::hana diff --git a/test/string.cpp b/test/string.cpp index b14c6ddd4..c6aeadbe0 100644 --- a/test/string.cpp +++ b/test/string.cpp @@ -30,11 +30,13 @@ using namespace boost::hana; namespace boost { namespace hana { namespace test { template <> auto instances = tuple( - type - , type + type + + , type + , type + , type , type - , type , type ); @@ -377,5 +379,31 @@ int main() { just(char_<'d'>) )); } + + // elem + { + struct invalid { }; + BOOST_HANA_CONSTANT_CHECK(elem(BOOST_HANA_STRING("abcd"), char_<'a'>)); + BOOST_HANA_CONSTANT_CHECK(elem(BOOST_HANA_STRING("abcd"), char_<'c'>)); + BOOST_HANA_CONSTANT_CHECK(not_(elem(BOOST_HANA_STRING("abcd"), char_<'e'>))); + BOOST_HANA_CONSTANT_CHECK(not_(elem(BOOST_HANA_STRING("abcd"), invalid{}))); + } + + // lookup + { + struct invalid { }; + BOOST_HANA_CONSTANT_CHECK(equal( + lookup(BOOST_HANA_STRING("abcd"), char_<'a'>), + just(char_<'a'>) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + lookup(BOOST_HANA_STRING("abcd"), char_<'c'>), + just(char_<'c'>) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + lookup(BOOST_HANA_STRING("abcd"), invalid{}), + nothing + )); + } } }