Search code examples
c++gccclangc++20constexpr

How to print non-structural results of constexpr functions at compile time with clang++?


I'm writing a constexpr code and I would like to inspect computed values at compile time.

The usual trick is to do something like this:


struct ReturnValue
{
    int value1;
    int value2;
};

constexpr ReturnValue doTheComputation() 
{
    return { .value1 = 10, .value2 = 20 };
}


template< auto t > struct p;

p< doTheComputation() > foo;

This gives a nice compile error:

implicit instantiation of undefined template 'p<ReturnValue{10, 20}>'

and I can see the value of the result.

However, this does not work for non-structural types, like std::string_view, std::string, or anything that resembles a string, as such types are not allowed to be NTTP.

Non-working example with std::string_view: https://godbolt.org/z/da8W557nK

Non-working example with char const *: https://godbolt.org/z/5Mvqfx95q

Even if I use char const[ some_large_numer ] to ensure the message fits, like this, instead of a string, I get the ASCII values listed as template parameters:

implicit instantiation of undefined template 'p<ReturnValue{{115, 111, 109, 101, 32, 115, 101, 99, 114, 101, ...}}>

Are there any tips or tricks to print values of non-structural types at compile-time (as results of constexpr functions)?

I've seen an unofficial patch for GCC that apparently solves that (I haven't tested it), but I'm searching for a solution for clang (Apple-clang from Xcode 15 or newer and LLVM 18 or newer).


Solution

  • Inspired by the idea by @WeijunZhou from comments to the question, I searched more. I discovered that C++26 has a flavor of static_assert that allows the second parameter to be a string-like result of constexpr computation (C++ reference).

    Unfortunately, std::format is not constexpr-friendly even in C++26 (cpp-reference confirms this), but I can always write my formatting function that outputs string-like literal object (according to the specification, it only needs to provide .size() and .data() functions), so I came up with the following solution:

    #include <string_view>
    #include <string>
    
    struct ReturnValue
    {
        std::string_view data;
        std::string      data2;
    };
    
    constexpr ReturnValue doTheComputation() 
    {
        return { .data = "some secret message", .data2 = "owned data" };
    }
    
    constexpr std::string format( ReturnValue const & val )
    {
        std::string result{ val.data };
        result += ", additional data: ";
        result += val.data2;
        return result;
    }
    
    static_assert( false, format( doTheComputation() ) );
    

    This appears to work on LLVM 18, but not yet on Apple-Clang shipped with Xcode 15.4 (I still need to test with Xcode 16 beta).

    In general, it's a workable solution, but I would still be happier if somebody came up with a solution that I could use on Xcode 15.4.