From dc56f6cebcc42fa7b83949f754bc8ae252aba807 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Sat, 2 May 2015 14:43:10 -0400 Subject: [PATCH] [String] Add conversion to `char const*` and minor refactoring Also - remove useless value_type alias in String since it's not a Constant - document why the interface is so minimal --- example/misc/printf.cpp | 10 +- example/string.cpp | 143 ++++++++++++++----------- example/struct.mcd.adapt_macro.cpp | 2 +- example/struct.mcd.define_macro.cpp | 2 +- example/struct.mcd.nested.cpp | 2 +- example/struct.mcd.tag_dispatching.cpp | 2 +- include/boost/hana/fwd/string.hpp | 34 ++++-- include/boost/hana/string.hpp | 17 +++ test/string.cpp | 39 +++++++ 9 files changed, 169 insertions(+), 82 deletions(-) diff --git a/example/misc/printf.cpp b/example/misc/printf.cpp index dcb3f822f..a88057b0b 100644 --- a/example/misc/printf.cpp +++ b/example/misc/printf.cpp @@ -50,14 +50,6 @@ constexpr auto format(Tokens ...tokens_) { return prepend(format_string, variables); } - -template -char const string_literal[] = {s..., '\0'}; - -template -auto to_string_literal(_string const&) -{ return string_literal; } - int main() { int a = 1; float b = 1.3; @@ -70,6 +62,6 @@ int main() { ); unpack(args, [](auto fmt, auto ...args) { - std::printf(to_string_literal(fmt), args...); + std::printf(to(fmt), args...); }); } diff --git a/example/string.cpp b/example/string.cpp index 1da63c62b..a210b03da 100644 --- a/example/string.cpp +++ b/example/string.cpp @@ -17,10 +17,85 @@ using namespace boost::hana; int main() { +////////////////////////////////////////////////////////////////////////////// +// Modeled concepts +////////////////////////////////////////////////////////////////////////////// +{ + +//! [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] + +}{ + +//! [Foldable] +auto sum_string = [](auto str) { + return fold.left(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( + find(BOOST_HANA_STRING("abcxefg"), char_<'x'>) == just(char_<'x'>) +); +//! [Searchable] + +} + +////////////////////////////////////////////////////////////////////////////// +// Methods +////////////////////////////////////////////////////////////////////////////// { //! [BOOST_HANA_STRING] BOOST_HANA_CONSTEXPR_LAMBDA auto str = BOOST_HANA_STRING("abcdef"); +BOOST_HANA_CONSTANT_CHECK(str == string<'a', 'b', 'c', 'd', 'e', 'f'>); static_assert(std::is_same, String>{}, ""); //! [BOOST_HANA_STRING] @@ -33,69 +108,11 @@ static_assert(std::is_same, 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] - -}{ - -//! [foldable] -auto sum_string = [](auto str) { - return fold.left(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( - find(BOOST_HANA_STRING("abcxefg"), char_<'x'>) == just(char_<'x'>) -); -//! [searchable] +//! [to] +constexpr auto str = string<'h', 'i'>; +constexpr char const* s = to(str); +static_assert(s[0] == 'h' && s[1] == 'i' && s[2] == '\0', ""); +//! [to] } diff --git a/example/struct.mcd.adapt_macro.cpp b/example/struct.mcd.adapt_macro.cpp index 750530e20..15fecf827 100644 --- a/example/struct.mcd.adapt_macro.cpp +++ b/example/struct.mcd.adapt_macro.cpp @@ -43,7 +43,7 @@ int main() { BOOST_HANA_RUNTIME_CHECK(find(john, BOOST_HANA_STRING("name")) == just("John")); BOOST_HANA_RUNTIME_CHECK(find(john, BOOST_HANA_STRING("age")) == just(30)); - BOOST_HANA_CONSTANT_CHECK(find(john, BOOST_HANA_STRING("not_a_member")) == nothing); + BOOST_HANA_CONSTANT_CHECK(find(john, BOOST_HANA_STRING("foo")) == nothing); BOOST_HANA_RUNTIME_CHECK(to(john) == make( make(BOOST_HANA_STRING("name"), "John"), diff --git a/example/struct.mcd.define_macro.cpp b/example/struct.mcd.define_macro.cpp index 18c744a17..841202f54 100644 --- a/example/struct.mcd.define_macro.cpp +++ b/example/struct.mcd.define_macro.cpp @@ -38,7 +38,7 @@ int main() { BOOST_HANA_RUNTIME_CHECK(find(john, BOOST_HANA_STRING("name")) == just("John")); BOOST_HANA_RUNTIME_CHECK(find(john, BOOST_HANA_STRING("age")) == just(30)); - BOOST_HANA_CONSTANT_CHECK(find(john, BOOST_HANA_STRING("not_a_member")) == nothing); + BOOST_HANA_CONSTANT_CHECK(find(john, BOOST_HANA_STRING("foo")) == nothing); BOOST_HANA_RUNTIME_CHECK(to(john) == make( make(BOOST_HANA_STRING("name"), "John"), diff --git a/example/struct.mcd.nested.cpp b/example/struct.mcd.nested.cpp index 561dd1150..c63697e60 100644 --- a/example/struct.mcd.nested.cpp +++ b/example/struct.mcd.nested.cpp @@ -48,7 +48,7 @@ int main() { BOOST_HANA_RUNTIME_CHECK(find(john, BOOST_HANA_STRING("name")) == just("John")); BOOST_HANA_RUNTIME_CHECK(find(john, BOOST_HANA_STRING("age")) == just(30)); - BOOST_HANA_CONSTANT_CHECK(find(john, "clearly not a member") == nothing); + BOOST_HANA_CONSTANT_CHECK(find(john, BOOST_HANA_STRING("foo")) == nothing); BOOST_HANA_RUNTIME_CHECK(to(john) == make( make(BOOST_HANA_STRING("name"), "John"), diff --git a/example/struct.mcd.tag_dispatching.cpp b/example/struct.mcd.tag_dispatching.cpp index a3636e98a..46ea47dcf 100644 --- a/example/struct.mcd.tag_dispatching.cpp +++ b/example/struct.mcd.tag_dispatching.cpp @@ -53,7 +53,7 @@ int main() { BOOST_HANA_RUNTIME_CHECK(find(john, name) == just("John")); BOOST_HANA_RUNTIME_CHECK(find(john, age) == just(30)); - BOOST_HANA_CONSTANT_CHECK(find(john, "clearly not a member") == nothing); + BOOST_HANA_CONSTANT_CHECK(find(john, BOOST_HANA_STRING("foo")) == nothing); BOOST_HANA_RUNTIME_CHECK(to(john) == make( make(name, "John"), diff --git a/include/boost/hana/fwd/string.hpp b/include/boost/hana/fwd/string.hpp index 2fa1542f7..eac322b48 100644 --- a/include/boost/hana/fwd/string.hpp +++ b/include/boost/hana/fwd/string.hpp @@ -14,6 +14,18 @@ namespace boost { namespace hana { //! @ingroup group-datatypes //! Represents a compile-time string. //! + //! Conceptually, a `String` is like a `Tuple` holding `IntegralConstant`s + //! of type `char`. However, the interface of a `String` is not as rich as + //! that of a `Tuple`, because a `String` can only hold objects of a + //! single generalized type. + //! + //! `String`s are used for simple purposes like being keys in a `Map` or + //! tagging the members of a `Struct`. However, you might find that + //! `String` does not provide enough functionality to be used as a + //! full-blown compile-time string implementation. Indeed, providing + //! a comprehensive string interface is a lot of job, and it is out + //! of the scope of the library for the time being. + //! //! //! Modeled concepts //! ---------------- @@ -23,27 +35,37 @@ namespace boost { namespace hana { //! 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 + //! @snippet example/string.cpp Comparable //! //! 2. `Orderable` (operators provided)\n //! The total order implemented for `Orderable` is the usual //! lexicographical comparison of strings. - //! @snippet example/string.cpp orderable + //! @snippet example/string.cpp Orderable //! //! 3. `Foldable`\n //! Folding a `String` is equivalent to folding the sequence of its //! characters. - //! @snippet example/string.cpp foldable + //! @snippet example/string.cpp Foldable //! //! 4. `Iterable` (operators provided)\n //! Iterating over a `String` is equivalent to iterating over the sequence //! of its characters. - //! @snippet example/string.cpp iterable + //! @snippet example/string.cpp Iterable //! //! 5. `Searchable`\n //! Searching through a `String` is equivalent to searching through the //! sequence of its characters. - //! @snippet example/string.cpp searchable + //! @snippet example/string.cpp Searchable + //! + //! + //! Conversion to `char const*` + //! --------------------------- + //! A `String` can be converted to a `constexpr` null-delimited string of + //! type `char const*` by using `to`. This makes it easy to + //! turn a compile-time string into a runtime string. However, note that + //! this conversion is not an embedding, because `char const*` does not + //! model the same concepts as `String` does. + //! @snippet example/string.cpp to //! //! //! > #### Rationale for `String` not being a `Constant` @@ -64,7 +86,7 @@ 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 { using value_type = char const*; }; + struct String { }; //! Create a compile-time string from a parameter pack of characters. //! @relates String diff --git a/include/boost/hana/string.hpp b/include/boost/hana/string.hpp index 59cc38092..f7a03d66f 100644 --- a/include/boost/hana/string.hpp +++ b/include/boost/hana/string.hpp @@ -14,6 +14,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include +#include #include #include #include @@ -79,6 +80,22 @@ namespace boost { namespace hana { : operators::of { }; + ////////////////////////////////////////////////////////////////////////// + // to + ////////////////////////////////////////////////////////////////////////// + template <> + struct to_impl { + template + static constexpr char const c_string[sizeof...(c) + 1] = {c..., '\0'}; + + template + static constexpr char const* apply(_string const&) + { return c_string; } + }; + + template + constexpr char const to_impl::c_string[sizeof...(c) + 1]; + ////////////////////////////////////////////////////////////////////////// // Comparable ////////////////////////////////////////////////////////////////////////// diff --git a/test/string.cpp b/test/string.cpp index 5c023d021..63aa148e8 100644 --- a/test/string.cpp +++ b/test/string.cpp @@ -22,6 +22,7 @@ Distributed under the Boost Software License, Version 1.0. #include #include +#include #include using namespace boost::hana; @@ -52,6 +53,44 @@ int main() { >::value, ""); } + ////////////////////////////////////////////////////////////////////////// + // to + ////////////////////////////////////////////////////////////////////////// + { + static_assert(is_convertible{}, ""); + static_assert(!is_embedded{}, ""); + + BOOST_HANA_RUNTIME_ASSERT(std::strcmp( + to(BOOST_HANA_STRING("")), + "" + ) == 0); + + BOOST_HANA_RUNTIME_ASSERT(std::strcmp( + to(BOOST_HANA_STRING("a")), + "a" + ) == 0); + + BOOST_HANA_RUNTIME_ASSERT(std::strcmp( + to(BOOST_HANA_STRING("ab")), + "ab" + ) == 0); + + BOOST_HANA_RUNTIME_ASSERT(std::strcmp( + to(BOOST_HANA_STRING("abc")), + "abc" + ) == 0); + + BOOST_HANA_RUNTIME_ASSERT(std::strcmp( + to(BOOST_HANA_STRING("abcd")), + "abcd" + ) == 0); + + // make sure we can turn a non-constexpr String + // into a constexpr char const* + auto str = BOOST_HANA_STRING("abcdef"); + constexpr char const* c_str = to(str); (void)c_str; + } + ////////////////////////////////////////////////////////////////////////// // Comparable //////////////////////////////////////////////////////////////////////////