Sorry that this will be a long post, but I feel like you need all of the code to see what's going on.
So, I have been experimenting with an idea for compile time string to data structure parser. Think of something like a regex, where the string is "compiled" into a data structure at compile time but executed at runtime (so long as the input string is a constant of course). But I've run into an issue that I don't quite understand what's wrong:
Basically, my design is a 2 pass parser:
Here's what things look like:
// a class to wrap string constants
class constexpr_string {
public:
    template <size_t N>
    constexpr constexpr_string(const char (&s)[N]) : string_(s), size_(N - 1) {}
public:
    constexpr size_t size() const     { return size_; }
    constexpr size_t capacity() const { return size(); }
    constexpr size_t empty() const    { return size() != 0; }
public:
    constexpr char operator[](size_t n) const { return string_[n]; }
private:
    const char *string_;
    size_t      size_;
};
// would have loved to use std::array, but ran into an issue so..
// wrapped in a struct so we can return it
template <class T, size_t N>
struct constexpr_array {
    T array[N] = {};
};
struct opcode { /* not relevant */ };
template <size_t N>
constexpr constexpr_array<opcode, N> compile_string(constexpr_string fmt) {
    constexpr_array<opcode, N> compiled;
    /* fill in compiled_format */
    return compiled;
}
constexpr size_t calculate_size(constexpr_string fmt) {
    size_t size = 0;
    /* calculate size */
    return size;
}
#if 0
// NOTE: Why doesn't **This** work?
constexpr int test(constexpr_string input) {
    constexpr size_t compiled_size = calculate_size(input);
    constexpr auto compiled_format = compile_string<compiled_size>(input);
    return 0;
}
#endif
int main() {
    // NOTE: when this works...
    constexpr char input[] = "...";
    constexpr size_t compiled_size = calculate_size(input);
    constexpr auto compiled = compile_string<compiled_size>(input);
    execute(compiled); // run it!
}
So far so good!
The problem arises when I try to just wrap those 2 lines into a function :-/.
I don't understand why the same exact code works in main, but if I just try to pass the same
constexpr object to another function, I start getting errors about things not being constexpr.
Here's the error message:
main.cpp: In function ‘constexpr int test(constexpr_string)’:
main.cpp:258:55: error: ‘input’ is not a constant expression
  constexpr size_t compiled_size = calculate_size(input);
                                                       ^
main.cpp:259:70: error: no matching function for call to ‘compile_string<compiled_size>(constexpr_string&)’
  constexpr auto compiled_format = compile_string<compiled_size>(input);
                                                                      ^
main.cpp:60:45: note: candidate: template<long unsigned int N> constexpr constexpr_array<opcode, N> compile_string(constexpr_string)
 constexpr constexpr_array<opcode, N> compile_string(constexpr_string fmt) {
                                             ^~~~~~~~~~~~~~
main.cpp:60:45: note:   template argument deduction/substitution failed:
                The keyword constexpr was introduced in C++11 and improved in C++14. It means constant expression. Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors.
A constexpr function that is eligible to be evaluated at compile-time will only be evaluated at compile-time if the return value is used where a constant expression is required. Otherwise, compile-time evaluation is not guaranteed.
constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well.
Basically C++'s constexpr is Rust's const , which can be directly defined within a block (no need for the callable trick).
Let's reduce this:
constexpr void f(int i) {
    constexpr int j = i; // error
}
int main() {
    constexpr int i = 0;
    constexpr int j = i; // OK
}
Function parameters are never constexpr, so i inside f is not a constant expression and can't be used to initialize j. Once you pass something through a function parameter, the constexpr-ness is lost.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With