跳到主要内容
左猫娘右猫娘

MSVC错误 编译期缓存错误和ICE

· 阅读需 5 分钟
Heng_Xin
ここから先は一方通行だ!

之前在写反射的时候, 遇到的这个问题; 一开始以为是我自己写错了, 排查了半天, 从 VsCode 换到 MSVC...

最后写了最小复现案例, 然后才发现就是 MSVC sabi!

描述

请见 https://github.com/HengXin666/HXLibs/issues/7

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest.h>

#include <HXLibs/reflection/MemberName.hpp>
#include <HXLibs/reflection/ReflectionMacro.hpp>
#include <HXLibs/log/Log.hpp>

using namespace HX;

TEST_CASE("对照组: 无宏反射") {
struct Case01 {
int a;
std::string b;
std::vector<Case01> c;
};

struct Case02 {
int num;
std::string str;
std::vector<Case02> arr;
};

constexpr auto N = reflection::membersCountVal<Case01>;
constexpr auto name = reflection::getMembersNames<Case01>();

CHECK(N == 3);
CHECK(name[0] == "a");
CHECK(name[1] == "b");
CHECK(name[2] == "c");

constexpr auto N2 = reflection::membersCountVal<Case02>;
constexpr auto name2 = reflection::getMembersNames<Case02>();

CHECK(N2 == 3);
CHECK(name2[0] == "num");
CHECK(name2[1] == "str");
CHECK(name2[2] == "arr");
}
cpp

在线实验: https://godbolt.org/z/zzaWsP5j7

#include <variant>
#include <optional>
#include <memory>
#include <type_traits>

namespace HX::meta {

/**
* @brief 删除 T 类型的 const、引用、v 修饰
* @tparam T
*/
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;

} // !namespace

using namespace HX;

/**
* @brief 对于类型 T, 提供类静态成员
* @tparam T
*/
template <typename T>
struct StaticObj {
inline static meta::remove_cvref_t<T> obj;
};

/**
* @brief 获取类静态成员T
* @tparam T
* @return constexpr utils::remove_cvref_t<T>&
*/
template <typename T>
inline constexpr meta::remove_cvref_t<T>& getStaticObj() {
return StaticObj<T>::obj;
}

/**
* @brief 访问者类 主模版 (不可使用)
* @tparam T `聚合类`类型
* @tparam N 成员个数
* @warning 如果匹配到此模版, 那么有两种可能:
* @warning 1) 你的类有255+个成员
* @warning 2) 你的类不是聚合类
*/
template <typename T, std::size_t N>
struct ReflectionVisitor {
static constexpr auto visit() {
static_assert(!sizeof(T), "");
}
};

/**
* @brief 偏特化模版生成 工具宏
*/
#define _HX_GENERATE_TEMPLATES_WITH_SPECIALIZATION_(N, ...) \
template <typename T> \
struct ReflectionVisitor<T, N> { \
static constexpr auto visit() { \
auto& [__VA_ARGS__] = getStaticObj<T>(); \
auto t = std::tie(__VA_ARGS__); \
constexpr auto f = [](auto&... fs) { \
return std::make_tuple((&fs)...); \
}; \
return std::apply(f, t); \
} \
};

_HX_GENERATE_TEMPLATES_WITH_SPECIALIZATION_(1, f0);
_HX_GENERATE_TEMPLATES_WITH_SPECIALIZATION_(2, f0, f1);
_HX_GENERATE_TEMPLATES_WITH_SPECIALIZATION_(3, f0, f1, f2);
_HX_GENERATE_TEMPLATES_WITH_SPECIALIZATION_(4, f0, f1, f2, f3);

template <std::size_t N, typename T>
inline constexpr auto getStaticObjPtrTuple() {
return ReflectionVisitor<meta::remove_cvref_t<T>, N>::visit();
}

#include <string>
#include <vector>
#include <iostream>

template <auto ptr>
inline constexpr std::string_view getMemberName() {
#if defined(_MSC_VER)
constexpr std::string_view funcName = __FUNCSIG__;
#else
constexpr std::string_view funcName = __PRETTY_FUNCTION__;
#endif
return funcName;
}

template <class T>
struct Wrapper {
using Type = T;
T v;
};

template <class T>
Wrapper(T) -> Wrapper<T>;

// This workaround is necessary for clang. or msvc
template <class T>
inline constexpr auto wrap(const T& arg) noexcept {
return Wrapper{arg};
}

int main() {
struct Case01 {
int a;
std::string b;
std::vector<Case01> c;
};

struct Case02 {
int num;
std::string str;
std::vector<Case02> arr;
};

constexpr auto tp1 = getStaticObjPtrTuple<3, Case01>();
constexpr auto tp2 = getStaticObjPtrTuple<3, Case02>();
// clang 17 <=, 就会报错
// msvc 得到的是 a 和 a, 而不是 a 和 num
std::cout << getMemberName<get<0>(tp1)>() << '\n';
std::cout << getMemberName<get<0>(tp2)>() << '\n';

// 需要套一层
std::cout << getMemberName<wrap(get<0>(tp1))>() << '\n';
std::cout << getMemberName<wrap(get<0>(tp2))>() << '\n';
return 0;
}
cpp

发现套一层就好了~

还有高手

当我准备整个最小可复现案例的时候:

我靠, 直接 Internal compiler error (ICE) 了...

然后我只好反馈给 MSVC 了: https://developercommunity.visualstudio.com/t/MSVC-ICE-when-using-templated-auto-param/10944095

请作者喝奶茶:
Alipay IconQR Code
Alipay IconQR Code
本文遵循 CC CC 4.0 BY-SA 版权协议, 转载请标明出处
Loading Comments...