Passing strings as a template parameter (small utility)

Now that I think about it some more, there's really nothing stopping us from taking this much farther -- access individual characters, calculate the length, slice and substr, even search and replace. There's no reason why ctstring<> can't implement most of the std::string interface at compile time.
This would be great showcase of the advanced work being done by boost developers. This would have a "high" wow factor. I bet, the vast horde of "old-style" c++ developers what immediately step up and take notice.

Tom Brinkman wrote:
Now that I think about it some more, there's really nothing stopping us from taking this much farther -- access individual characters, calculate the length, slice and substr, even search and replace. There's no reason why ctstring<> can't implement most of the std::string interface at compile time.
This would be great showcase of the advanced work being done by boost developers. This would have a "high" wow factor.
I bet, the vast horde of "old-style" c++ developers what immediately step up and take notice.
I fleshed out the compile-time string class, and called it mpl::string. (See attached.) It is a Front and Back Extensible, Random-Access MPL Sequence. The primary use is as follows: template<char const *sz> // template on a string struct foo { void bar() { std::printf("%s\n", sz); } }; foo< mpl::string<'hell','o wo','rld!'>::c_str > f; f.bar(); // prints "hello world!" Is there interest in adding this to the MPL? -- Eric Niebler Boost Consulting www.boost-consulting.com /////////////////////////////////////////////////////////////////////////////// // // Copyright 2008 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include <string> #include <cstring> #include <iostream> #include <boost/mpl/at.hpp> #include <boost/mpl/long.hpp> #include <boost/mpl/back.hpp> #include <boost/mpl/copy.hpp> #include <boost/mpl/size.hpp> #include <boost/mpl/empty.hpp> #include <boost/mpl/front.hpp> #include <boost/mpl/clear.hpp> #include <boost/mpl/erase.hpp> #include <boost/mpl/insert.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/size_t.hpp> #include <boost/mpl/for_each.hpp> #include <boost/mpl/vector_c.hpp> #include <boost/mpl/push_back.hpp> #include <boost/mpl/joint_view.hpp> #include <boost/mpl/insert_range.hpp> #include <boost/mpl/back_inserter.hpp> #include <boost/mpl/iterator_range.hpp> #include <boost/preprocessor/arithmetic/dec.hpp> #include <boost/preprocessor/arithmetic/div.hpp> #include <boost/preprocessor/repetition/enum.hpp> #include <boost/preprocessor/punctuation/comma_if.hpp> #include <boost/preprocessor/repetition/enum_params.hpp> #include <boost/preprocessor/repetition/repeat_from_to.hpp> #include <boost/preprocessor/repetition/enum_shifted_params.hpp> #include <boost/preprocessor/repetition/enum_trailing_params.hpp> #include <boost/preprocessor/repetition/enum_params_with_a_default.hpp> // Define mpl::char_ #include <boost/mpl/aux_/adl_barrier.hpp> #include <boost/mpl/aux_/nttp_decl.hpp> BOOST_MPL_AUX_ADL_BARRIER_NAMESPACE_OPEN template< BOOST_MPL_AUX_NTTP_DECL(char, N) > struct char_; BOOST_MPL_AUX_ADL_BARRIER_NAMESPACE_CLOSE BOOST_MPL_AUX_ADL_BARRIER_DECL(char_) #define AUX_WRAPPER_VALUE_TYPE char #include <boost/mpl/aux_/integral_wrapper.hpp> #include <boost/detail/lightweight_test.hpp> namespace boost { namespace mpl { //#define BOOST_MPL_STRING_MAX_LENGTH 128 #define BOOST_MPL_STRING_MAX_LENGTH 32 #define BOOST_MPL_STRING_MAX_PARAMS BOOST_PP_DIV(BOOST_MPL_STRING_MAX_LENGTH, 4) #define BOOST_MPL_MULTICHAR_LENGTH(c) ((std::size_t)((c>0xffffff)+(c>0xffff)+(c>0xff)+1)) #define BOOST_MPL_MULTICHAR_AT(c,i) (char)((c)>>(8*(BOOST_MPL_MULTICHAR_LENGTH(c)-((std::size_t)(i))-1))) struct string_tag; struct string_iterator_tag; template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C, 0)> struct string; template<typename Sequence, long N> struct string_iterator; template<typename Sequence> struct sequence_tag; template<typename Tag> struct size_impl; template<> struct size_impl<string_tag> { template<typename Sequence> struct apply : mpl::size_t<Sequence::size> {}; }; template<typename Tag> struct at_impl; template<> struct at_impl<string_tag> { template<typename Sequence, typename N> struct apply : Sequence::template at<N::value> {}; }; template<typename Tag> struct begin_impl; template<> struct begin_impl<string_tag> { template<typename Sequence> struct apply { typedef string_iterator<Sequence, 0> type; }; }; template<typename Tag> struct end_impl; template<> struct end_impl<string_tag> { template<typename Sequence> struct apply { typedef string_iterator<Sequence, Sequence::size> type; }; }; template<typename Tag> struct push_back_impl; template<> struct push_back_impl<string_tag> { template<typename Sequence, typename Value> struct apply; template<typename Value> struct apply<string<>, Value> { typedef string<(char)Value::value> type; }; #define M0(z,n,data) \ template<BOOST_PP_ENUM_PARAMS_Z(z, n, unsigned int C), typename Value> \ struct apply<string<BOOST_PP_ENUM_PARAMS_Z(z, n, C)>, Value> \ { \ typedef string< \ BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_DEC(n), C) \ BOOST_PP_COMMA_IF(BOOST_PP_DEC(n)) \ (BOOST_PP_CAT(C,BOOST_PP_DEC(n))>0xffffff) \ ?BOOST_PP_CAT(C,BOOST_PP_DEC(n)) \ :(BOOST_PP_CAT(C,BOOST_PP_DEC(n))<<8)|(unsigned char)Value::value \ , (BOOST_PP_CAT(C,BOOST_PP_DEC(n))>0xffffff) \ ?(char)Value::value \ :0 \ > type; \ }; BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_DEC(BOOST_MPL_STRING_MAX_PARAMS), M0, ~) #undef M0 template<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C), typename Value> struct apply<string<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C)>, Value> { BOOST_MPL_ASSERT_RELATION(BOOST_PP_CAT(C,BOOST_PP_DEC(BOOST_MPL_STRING_MAX_PARAMS)), <=, 0xffffff); typedef string< BOOST_PP_ENUM_PARAMS(BOOST_PP_DEC(BOOST_MPL_STRING_MAX_PARAMS), C) , (BOOST_PP_CAT(C,BOOST_PP_DEC(BOOST_MPL_STRING_MAX_PARAMS))<<8)|(unsigned char)Value::value > type; }; }; template<typename Tag> struct push_front_impl; template<> struct push_front_impl<string_tag> { template<typename Sequence, typename Value, std::size_t N = BOOST_MPL_MULTICHAR_LENGTH(Sequence::head_)> struct apply; template<typename Value> struct apply<string<>, Value, 1> { typedef string<(char)Value::value> type; }; #define M0(z,n,data) \ template<BOOST_PP_ENUM_PARAMS_Z(z, n, unsigned int C), typename Value, std::size_t N> \ struct apply<string<BOOST_PP_ENUM_PARAMS_Z(z, n, C)>, Value, N> \ { \ typedef string< \ ((((unsigned char)Value::value)<<(N*8))|C0) \ BOOST_PP_COMMA_IF(BOOST_PP_DEC(n)) \ BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z, n, C) \ > type; \ }; \ template<BOOST_PP_ENUM_PARAMS_Z(z, n, unsigned int C), typename Value> \ struct apply<string<BOOST_PP_ENUM_PARAMS_Z(z, n, C)>, Value, 4> \ { \ typedef string< \ (char)Value::value \ BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, C) \ > type; \ }; BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_DEC(BOOST_MPL_STRING_MAX_PARAMS), M0, ~) #undef M0 template<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C), typename Value, std::size_t N> struct apply<string<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C)>, Value, N> { BOOST_MPL_ASSERT_RELATION(C0, <=, 0xffffff); typedef string< (((unsigned char)Value::value)<<(N*8))|C0 , BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C) > type; }; }; template<typename Tag> struct insert_range_impl; template<> struct insert_range_impl<string_tag> { template<typename Sequence, typename Pos, typename Range> struct apply : copy< joint_view< iterator_range< string_iterator<Sequence, 0> , Pos > , joint_view< Range , iterator_range< Pos , string_iterator<Sequence, Sequence::size> > > > , back_inserter<string<> > > {}; }; template<typename Tag> struct insert_impl; template<> struct insert_impl<string_tag> { template<typename Sequence, typename Pos, typename Value> struct apply : insert_range<Sequence, Pos, string<(char)Value::value> > {}; }; template<typename Tag> struct erase_impl; template<> struct erase_impl<string_tag> { template<typename Sequence, typename First, typename Last> struct apply : copy< joint_view< iterator_range< string_iterator<Sequence, 0> , First > , iterator_range< typename if_na<Last, typename next<First>::type>::type , string_iterator<Sequence, Sequence::size> > > , back_inserter<string<> > > {}; }; template<typename Tag> struct clear_impl; template<> struct clear_impl<string_tag> { template<typename> struct apply { typedef string<> type; }; }; template<typename Tag> struct advance_impl; template<> struct advance_impl<string_iterator_tag> { template<typename Iterator, typename N> struct apply { typedef string_iterator< typename Iterator::string_type , Iterator::index + N::value > type; }; }; template<typename Tag> struct distance_impl; template<> struct distance_impl<string_iterator_tag> { template<typename First, typename Last> struct apply { typedef mpl::long_<Last::index - First::index> type; }; }; template<typename Sequence, long N> struct string_iterator : Sequence::template at<N> { typedef string_iterator_tag tag; typedef std::random_access_iterator_tag category; typedef Sequence string_type; static long const index = N; typedef string_iterator<Sequence, N+1> next; typedef string_iterator<Sequence, N-1> prior; }; template<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C)> struct string { /// INTERNAL ONLY static unsigned int const head_ = C0; /// INTERNAL ONLY typedef string<BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C)> tail_; typedef string_tag tag; static std::size_t const size = BOOST_MPL_MULTICHAR_LENGTH(C0) + tail_::size; template<long Pos, bool B = (Pos < BOOST_MPL_MULTICHAR_LENGTH(C0))> struct at : boost::mpl::char_<BOOST_MPL_MULTICHAR_AT(C0,Pos)> {}; template<long Pos> struct at<Pos, false> : tail_::template at<Pos-BOOST_MPL_MULTICHAR_LENGTH(C0)> {}; static char const c_str[]; }; template<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C)> char const string<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C)>::c_str[] = { #define M0(z, n, data) at<n>::value BOOST_PP_ENUM(BOOST_MPL_STRING_MAX_LENGTH, M0, ~) #undef M0 , '\0' // to ensure the string is null-terminated }; template<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C)> std::size_t const string<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C)>::size; template<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, unsigned int C)> unsigned int const string<BOOST_PP_ENUM_PARAMS(BOOST_MPL_STRING_MAX_PARAMS, C)>::head_; template<> struct string<> { /// INTERNAL ONLY static unsigned int const head_ = 0; /// INTERNAL ONLY typedef string tail_; typedef string_tag tag; static std::size_t const size = 0; template<unsigned int> struct at : boost::mpl::char_<'\0'> {}; static char const c_str[]; }; char const string<>::c_str[] = {'\0'}; std::size_t const string<>::size; unsigned int const string<>::head_; }} // namespace boost using namespace boost; // Accept a string as a template parameter! template<char const *sz> struct greeting { std::string say_hello() const { return sz; } }; struct push_char { push_char(std::string &str) : str_(str) {} void operator()(char ch) const { this->str_.push_back(ch); } std::string &str_; }; int main() { BOOST_TEST(0 == std::strcmp(mpl::string<'Hell','o wo','rld!'>::c_str, "Hello world!")); BOOST_TEST((12 == mpl::size<mpl::string<'Hell','o wo','rld!'> >::type::value)); BOOST_TEST(('w' == mpl::at_c<mpl::string<'Hell','o wo','rld!'>, 6>::type::value)); // test using a string as a template parameter greeting<mpl::string<'Hell','o wo','rld!'>::c_str> g; BOOST_TEST("Hello world!" == g.say_hello()); BOOST_TEST(0 == std::strcmp("", mpl::string<>::c_str)); std::string result; mpl::for_each<mpl::string<'Hell','o wo','rld!'> >(push_char(result)); BOOST_TEST("Hello world!" == result); BOOST_MPL_ASSERT((mpl::empty<mpl::string<> >)); BOOST_MPL_ASSERT_NOT((mpl::empty<mpl::string<'hi!'> >)); BOOST_TEST(('h' == mpl::front<mpl::string<'hi!'> >::type())); BOOST_TEST(('!' == mpl::back<mpl::string<'hi!'> >::type())); // testing push_back { typedef mpl::push_back<mpl::string<>, mpl::char_<'a'> >::type t1; BOOST_TEST(0 == std::strcmp("a", t1::c_str)); typedef mpl::push_back<t1, mpl::char_<'b'> >::type t2; BOOST_TEST(0 == std::strcmp("ab", t2::c_str)); typedef mpl::push_back<t2, mpl::char_<'c'> >::type t3; BOOST_TEST(0 == std::strcmp("abc", t3::c_str)); typedef mpl::push_back<t3, mpl::char_<'d'> >::type t4; BOOST_TEST(0 == std::strcmp("abcd", t4::c_str)); typedef mpl::push_back<t4, mpl::char_<'e'> >::type t5; BOOST_TEST(0 == std::strcmp("abcde", t5::c_str)); typedef mpl::string<'aaaa','aaaa','aaaa','aaaa','aaaa','aaaa','aaaa','aaa'> almost_full; BOOST_TEST(0 == std::strcmp("aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaa", almost_full::c_str)); typedef mpl::push_back<almost_full, mpl::char_<'X'> >::type t6; BOOST_TEST(0 == std::strcmp("aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaX", t6::c_str)); } // testing push_front { typedef mpl::push_front<mpl::string<>, mpl::char_<'a'> >::type t1; BOOST_TEST(0 == std::strcmp("a", t1::c_str)); typedef mpl::push_front<t1, mpl::char_<'b'> >::type t2; BOOST_TEST(0 == std::strcmp("ba", t2::c_str)); typedef mpl::push_front<t2, mpl::char_<'c'> >::type t3; BOOST_TEST(0 == std::strcmp("cba", t3::c_str)); typedef mpl::push_front<t3, mpl::char_<'d'> >::type t4; BOOST_TEST(0 == std::strcmp("dcba", t4::c_str)); typedef mpl::push_front<t4, mpl::char_<'e'> >::type t5; BOOST_TEST(0 == std::strcmp("edcba", t5::c_str)); typedef mpl::string<'aaa','aaaa','aaaa','aaaa','aaaa','aaaa','aaaa','aaaa'> almost_full; BOOST_TEST(0 == std::strcmp("aaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa", almost_full::c_str)); typedef mpl::push_front<almost_full, mpl::char_<'X'> >::type t6; BOOST_TEST(0 == std::strcmp("Xaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa" "aaaa", t6::c_str)); } // back-inserter with copy typedef mpl::vector_c<char, 'a','b','c','d','e'> rgc; typedef mpl::copy<rgc, mpl::back_inserter<mpl::string<> > >::type str; BOOST_TEST(0 == std::strcmp("abcde", str::c_str)); // test insert_range and erase { typedef mpl::string<'Hell','o wo','rld!'> hello; typedef mpl::advance_c<mpl::begin<hello>::type, 5>::type where; typedef mpl::string<' cru','el'> cruel; typedef mpl::insert_range<hello, where, cruel>::type hello_cruel; BOOST_TEST(0 == std::strcmp("Hello cruel world!", hello_cruel::c_str)); typedef mpl::erase<hello, mpl::begin<hello>::type, where>::type erased1; BOOST_TEST(0 == std::strcmp(" world!", erased1::c_str)); } return report_errors(); }

On Sun, 23 Mar 2008 23:07:57 -0700 Eric Niebler <eric@boost-consulting.com> wrote:
Tom Brinkman wrote:
Now that I think about it some more, there's really nothing stopping us from taking this much farther -- access individual characters, calculate the length, slice and substr, even search and replace. There's no reason why ctstring<> can't implement most of the std::string interface at compile time.
This would be great showcase of the advanced work being done by boost developers. This would have a "high" wow factor.
I bet, the vast horde of "old-style" c++ developers what immediately step up and take notice.
I fleshed out the compile-time string class, and called it mpl::string. (See attached.) It is a Front and Back Extensible, Random-Access MPL Sequence. The primary use is as follows:
template<char const *sz> // template on a string struct foo { void bar() { std::printf("%s\n", sz); } };
foo< mpl::string<'hell','o wo','rld!'>::c_str > f; f.bar(); // prints "hello world!"
Is there interest in adding this to the MPL?
Yes please. Was looking at working your earlier post into something more elaborate ... but oh well - though have only skimmed over your present post. P.S. I presume you'd wrap the mpl_max_string_length around some ifdef for a user compile-time defined value. Please, let's get this into the mpl. Cheers, -- Manfred 24 March, 2008

foo< mpl::string<'hell','o wo','rld!'>::c_str > f;
Isn't 'hell' undefined behavior? :) Seriously though, aren't we limited to ASCII in ''? Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

Emil Dotchevski wrote:
foo< mpl::string<'hell','o wo','rld!'>::c_str > f;
Isn't 'hell' undefined behavior? :)
Multicharacter literals like 'hell' have an implementation-defined value. It should be possible to make this interface work, but that the implementation may be different for different compilers.
Seriously though, aren't we limited to ASCII in ''?
Yes. So? -- Eric Niebler Boost Consulting www.boost-consulting.com
participants (6)
-
Emil Dotchevski
-
Eric Niebler
-
Manfred Doudar
-
Phil Endecott
-
Sebastian Redl
-
Tom Brinkman