Say I have a metafunction that returns a std::string_view
template<typename T>
struct type_to_string;
template <typename T>
constexpr std::string_view type_to_string_v = type_to_string<T>::value;
and a string_view resulting from this:
constexpr auto sv = type_to_string_v<very_complex_expression>;
Is there any way to print the value of this string at compile-time? (ie. as part of some compiler error message or static assert)
CodePudding user response:
Here's a modification of OP's solution that produces a somewhat more readable message with gcc. Unfortunately, with clang the message is rather less readable, and MSVC doesn't work at all (due to a compiler bug I suppose).
#include <cstdlib>
#include <algorithm>
#include <string_view>
#include <utility>
template <size_t N> struct FS { // fixed size string
char chars[N 1] = {};
constexpr FS(const char (&str)[N 1]) {
std::copy_n(str, N 1, chars);
}
};
template <size_t N> FS(const char (&str)[N])->FS<N - 1>;
template<typename T>
struct type_to_string;
template<>
struct type_to_string<int> { constexpr static auto value = "integer"; };
template <auto>
constexpr bool string_value = false;
template<FS fs> // size will be deduced
void compile_time_display() {
static_assert(string_value<fs>);
}
int main()
{
constexpr std::string_view sv = type_to_string<int>::value;
constexpr size_t n = sv.size();
constexpr auto indices = std::make_index_sequence<n>();
[&] <std::size_t... I>
(std::index_sequence<I...>)
{
static constexpr char text[] { (char)sv.at(I)..., 0 };
compile_time_display<text>(); // the char array will convert to FS
}(indices);
}
The message shown by gcc reads:
main.cc: In instantiation of ‘void compile_time_display()
[with FS<...auto...> fs = FS<7>{"integer"}]’
...
main.cc:26:23: note: ‘string_value<FS<7>{"integer"}>’ evaluates to false
CodePudding user response:
C has no mechanism to "print" anything at compile-time. Constant evaluation has no input or output mechanism. static_assert
s can be used to display messages, but they specifically take string literals.
CodePudding user response:
It turns out that this can be done in a relatively straightforward way by destructuring the string_view
into a list of chars. Here's a complete example extending the setup from the original question:
#include <string_view>
#include <utility>
template<typename T>
struct type_to_string;
template<>
struct type_to_string<int> { constexpr static auto value = "integer"; };
template <char... T>
constexpr bool string_value = false;
template<char... c>
void compile_time_display() {
static_assert(string_value<c...>);
}
int main() {
constexpr std::string_view sv = type_to_string<int>::value;
constexpr size_t n = sv.size();
constexpr auto indices = std::make_index_sequence<n>();
[&] <std::size_t... I>
(std::index_sequence<I...>)
{
compile_time_display<sv.at(I)...>();
}(indices);
}
Popular compilers display the string value prominently:
$ g sv_to_array.cpp
sv_to_array.cpp: In instantiation of ‘void compile_time_display() [with char ...c = {'i', 'n', 't', 'e', 'g', 'e', 'r'}]’:
sv_to_array.cpp:26:36: required from ‘main()::<lambda(std::index_sequence<I ...>)> [with long unsigned int ...I = {0, 1, 2, 3, 4, 5, 6}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5, 6>]’
sv_to_array.cpp:27:3: required from here
sv_to_array.cpp:15:23: error: static assertion failed
15 | static_assert(string_value<c...>);
| ^~~~~~~~~~~~~~~~~~
sv_to_array.cpp:15:23: note: ‘string_value<'i', 'n', 't', 'e', 'g', 'e', 'r'>’ evaluates to false
$ clang sv_to_array.cpp -std=c 20
sv_to_array.cpp:15:2: error: static_assert failed due to requirement 'string_value<'i', 'n', 't', 'e', 'g', 'e', 'r'>'
static_assert(string_value<c...>);
^ ~~~~~~~~~~~~~~~~~~
sv_to_array.cpp:26:3: note: in instantiation of function template specialization 'compile_time_display<'i', 'n', 't', 'e', 'g', 'e', 'r'>' requested here
compile_time_display<sv.at(I)...>();
^
1 error generated.