如何用clang++在编译期打印constexpr函数的非结构化结果?
我正在编写constexpr代码,希望在编译期检查计算得出的值。
常用的技巧是通过未定义模板触发编译错误来查看结果,示例代码如下:
struct ReturnValue { int value1; int value2; }; constexpr ReturnValue doTheComputation() { return { .value1 = 10, .value2 = 20 }; } template< auto t > struct p; p< doTheComputation() > foo;
这会触发清晰的编译错误,从中可以直接看到计算结果:
implicit instantiation of undefined template 'p<ReturnValue{10, 20}>'
但这种方法对非结构化类型无效,比如std::string_view、std::string或任何类字符串类型——这类类型不允许作为非类型模板参数(NTTP)。即使改用固定大小的char const[]来容纳字符串,得到的也只是ASCII值序列,无法直接阅读:
implicit instantiation of undefined template 'p<ReturnValue{{115, 111, 109, 101, 32, 115, 101, 99, 114, 101, ...}}>'
请问有哪些技巧可以在编译期打印constexpr函数返回的非结构化类型值?我知道有一个针对GCC的非官方补丁可以解决,但我需要适用于Clang的方案(Xcode 15及以上版本的Apple-clang、LLVM 18及以上版本)。
适用于Clang的编译期打印方案
方案1:static_assert结合编译期字符串格式化
Clang支持在static_assert的错误消息中使用consteval函数生成的编译期字符串。你可以编写一个consteval格式化函数,将目标值转换为可读字符串后触发错误输出:
#include <string_view> #include <format> // 需要C++20及以上 constexpr std::string_view get_target_str() { return "hello constexpr world"; } consteval auto format_str(std::string_view sv) { return std::format("计算结果: {}", sv); } // 触发编译错误,查看结果 static_assert(false, format_str(get_target_str()));
编译时会输出包含目标字符串的错误信息:
static_assert failed due to requirement 'false' "计算结果: hello constexpr world"
对于包含字符串成员的自定义结构体,可以扩展格式化逻辑,递归处理成员:
#include <string_view> #include <format> struct DataHolder { std::string_view msg; int count; }; constexpr DataHolder compute_data() { return {"compiled string", 42}; } consteval auto format_holder(const DataHolder& holder) { return std::format("DataHolder{{msg: {}, count: {}}}", holder.msg, holder.count); } static_assert(false, format_holder(compute_data()));
Xcode 15的Apple-clang和LLVM 18均支持std::format的consteval调用。
方案2:Clang内置__builtin_dump_struct(快速实用)
Clang提供了非标准内置函数__builtin_dump_struct,能直接在编译期将结构体内容转换为可读字符串,无需手动编写格式化代码:
#include <string_view> struct ComplexData { std::string_view desc; double value; }; constexpr ComplexData get_complex_data() { return {"compile-time value", 3.14159}; } consteval auto dump_obj(const auto& obj) { return __builtin_dump_struct(&obj, ""); } static_assert(false, dump_obj(get_complex_data()));
编译时会输出详细的结构体内容,包含字符串成员的实际值,对类字符串类型支持良好。
方案3:封装通用打印工具函数
可以基于上述方法封装通用的编译期打印函数,适配不同类型:
#include <string_view> #include <type_traits> #include <string> template<typename T> consteval auto print_constexpr(const T& value) { if constexpr (std::is_same_v<T, std::string_view>) { return value; } else if constexpr (std::is_class_v<T>) { return __builtin_dump_struct(&value, ""); } else if constexpr (std::is_arithmetic_v<T>) { return std::to_string(value); } else { return "unsupported type"; } } constexpr std::string_view test_str() { return "compile check success"; } static_assert(false, print_constexpr(test_str()));
内容的提问来源于stack exchange,提问作者DoDo

