4

I decided to give then new C++14 definition of constexpr a spin and to get the most out of it I decided to write a little compile-time string parser. However, I'm struggling with keeping my object a constexpr while passing it to a function. Consider the following code:

#include <cstddef>
#include <stdexcept>

class str_const {
    const char * const p_;
    const std::size_t sz_;
public:
    template <std::size_t N>
    constexpr str_const( const char( & a )[ N ] )
    : p_( a ), sz_( N - 1 ) {}
    constexpr char operator[]( std::size_t n ) const {
        return n < sz_ ? p_[ n ] : throw std::out_of_range( "" );
    }
    constexpr std::size_t size() const { return sz_; }
};

constexpr long int numOpen( const str_const & str ){
    long int numOpen{ 0 };
    std::size_t idx{ 0 };
    while ( idx <  str.size() ){
        if ( str[ idx ] == '{' ){ ++numOpen; }
        else if ( str[ idx ] == '}' ){ --numOpen; }
        ++idx;
    }
    return numOpen;
}

constexpr bool check( const str_const & str ){
    constexpr auto nOpen = numOpen( str );
    // ...
    // Apply More Test functions here,
    // each returning a variable encoding the correctness of the input
    // ...

    return ( nOpen == 0 /* && ... Test the variables ... */ );
}

int main() {
    constexpr str_const s1{ "{ Foo : Bar } { Quooz : Baz }" };
    constexpr auto pass = check( s1 );
}

I uses the str_const class presented by Scott Schurr at C++Now 2012 in a version modified for C++14.

The above code will fail to compile with the error (clang-3.5)

error: constexpr variable 'nOpen' must be initialized by a constant expression  
    constexpr auto nOpen = numOpen( str );  
                           ~~~~~~~~~^~~~~

Which leads me to the conclusion that you can not pass around a constexpr object without losing its constexpr-ness. This lead me to the following questions:

  1. Is my interpretation correct?

  2. Why is this the behaviour the standard dictates?

    I don't see the problem in passing a constexpr object around. Sure, I could rewrite my code to fit into a single function, but that leads to cramped code. I would assume that factoring separate functionality into separate units of code (functions) should be good style for compile time operations too.

  3. As I said before the compiler error can be solved by moving the code from the bodies of separate testing functions (such as numOpen) into the body of the top-level function check. However, I don't like this solution since it creates one huge and cramped function. Do you see a different approach to solving the problem?

Community
  • 1
  • 1
elemakil
  • 3,681
  • 28
  • 53
  • 2
    *Do you see a different approach to solving the problem?* `constexpr auto nOpen = numOpen( str );` instance doesn't need to be `constexpr` to evaluate the function at compile time – Piotr Skotnicki Dec 04 '14 at 10:48

1 Answers1

7

The reason is that inside a constexpr function, parameters aren't constant expressions, regardless of whether the arguments are. You can call constexpr functions inside others, but the parameters of a constexpr function aren't constexpr inside, making any function call (even to constexpr functions) not a constant expression - inside.

const auto nOpen = numOpen( str );

Suffices. Only once you view the call from outside the constexpr-ness of the expressions inside is verified, deciding whether the whole call is constexpr or not.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • I guess this means that it is not possible to create a `constexpr` object inside a function from a string literal passed to that function? Which is a shame since I wanted to wrap the `check` function inside my actual parser. Something like [this](http://pastebin.com/CuG37r45) (non-compiling) mock-up code. – elemakil Dec 04 '14 at 12:26