Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-03-24 02:07:57


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();
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk