3

I got this compile time string comparison from another thread using constexpr and C++11 (http://stackoverflow.com/questions/5721813/compile-time-assert-for-string-equality). It works with constant strings like "OK"

    constexpr bool isequal(char const *one, char const *two) {
        return (*one && *two) ? (*one == *two && isequal(one + 1, two + 1))
        : (!*one && !*two);
    }

I am trying to use it in the following context:

 static_assert(isequal(boost::mpl::c_str<boost::mpl::string<'ak'>>::value, "ak"), "should not fail");

But it gives me an compilation error of static_assert expression is not an constant integral expression.

Can I do this?

Negative Zero
  • 1,224
  • 3
  • 10
  • 19

1 Answers1

1

The problem is that the value member of mpl::c_str is not marked as constexpr. Until the library authors decide to include support for constexpr, you are pretty much screwed, unless you are willing to modify your Boost code (or create your own version of c_str). If you decide to do so, the modification is quite simple: you just need to locate BOOST_ROOT/boost/mpl/string.hpp and replace this

template<typename Sequence>
struct c_str
{
    ...
    static typename Sequence::value_type const value[BOOST_MPL_LIMIT_STRING_SIZE+1] 
};

template<typename Sequence>
typename Sequence::value_type const c_str<Sequence>::value[BOOST_MPL_LIMIT_STRING_SIZE+1] =
{
    #define M0(z, n, data)                                                                      \
    mpl::aux_::deref_unless<BOOST_PP_CAT(i, n), iend>::type::value,
    BOOST_PP_REPEAT(BOOST_MPL_LIMIT_STRING_SIZE, M0, ~)
    #undef M0
    '\0'
};

by this

template<typename Sequence>
struct c_str
{
    ...
    static constexpr typename Sequence::value_type value[BOOST_MPL_LIMIT_STRING_SIZE+1] =
    {
        #define M0(z, n, data)                                                                      \
        mpl::aux_::deref_unless<BOOST_PP_CAT(i, n), iend>::type::value,
        BOOST_PP_REPEAT(BOOST_MPL_LIMIT_STRING_SIZE, M0, ~)
        #undef M0
        '\0'
    };
};

// definition still needed
template<typename Sequence>
constexpr typename Sequence::value_type c_str<Sequence>::value[BOOST_MPL_LIMIT_STRING_SIZE+1];

Hmm, after digging a bit more, it turns out the problem is more complex than I thought. In fact, static constants can be used in constexpr; the true problem is that c_str<T>::value is an array, and your function takes pointers as parameters. As a consequence, the compiler needs to decay the array, which boils down to taking the address of its first element. Since addresses are a runtime concept, it is not possible to take the address of an object in a constexpr.

To solve the issue, I tried to write a second version of isequal that operates on arrays rather than on pointers:

template <int N, int M>
constexpr bool isequal(char const (&one)[N], char const (&two)[M], int index)
{
    return (one[index] && two[index]) ?
        (one[index] == two[index] && isequal(one, two, index + 1)) :
        (!one[index] && !two[index]);
}

template <int N, int M>
constexpr bool isequal(char const (&one)[N], char const (&two)[M])
{
    // note: we can't check whether N == M since the size of the array
    // can be greater than the actual size of the null-terminated string
    return isequal(one, two, 0);
}

constexpr char hello[] = "hello";
static_assert(isequal(hello, hello), "hello == hello");
constexpr char zello[] = "zello";
static_assert(!isequal(hello, zello), "hello != zello");
constexpr char hel[] = "hel";
static_assert(!isequal(hello, hel), "hello != hel");

Unfortunately, this code does not work with mpl::c_str; in fact, the problem is that static const arrays are not compile-time values, unlike integral constants. So we're back the beginning: unless value is marked as constexpr, there is no way to use it in a constant expression.

As to why the code I gave initially fails, I can't answer right now as my version of gcc (4.6) fails to compile it altogether...

After updating gcc, it turns out value needs to be defined outside the class, even though it is declared and initialized in the class (see this question). I edited the code above with the correction.

Community
  • 1
  • 1
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • I am trying the code, it looks like the static_assert now works. But the sample code of BOOST MPL fails: typedef boost::mpl::string<'hell', 'o wo', 'rld'> hello; typedef boost::mpl::push_back >::type hello2; BOOST_ASSERT(0 == std::strcmp(boost::mpl::c_str::value, "hello world!")); – Negative Zero Jun 11 '12 at 18:46
  • It fails in the link time saying the boost::mpl::string<'hell', 'o wo', 'rld'> is not found – Negative Zero Jun 11 '12 at 18:48
  • Ok, I updated gcc, and I think the problem is that even if `value` is initialized in the class, it still needs to be defined outside (see [this question](http://stackoverflow.com/q/8016780/20984). I edited the code in my answer. – Luc Touraille Jun 11 '12 at 21:32