preferred_name
,例如使用在 libc++ 中将 std::basic_string<char>
拼写为 std::string
(这对用户更友好)。
我试图将它用于我自己的类型,但它似乎不起作用:https://gcc.godbolt.org/z/re4af1Pej
#include <iostream>
#include <string>
template <typename T>
void foo()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
template<typename T> struct my_basic_string;
using my_string = my_basic_string<char>;
template<typename T> struct __attribute__((__preferred_name__(my_string))) my_basic_string;
int main()
{
foo<std::string>(); // std::string
foo<my_string>(); // my_basic_string<char> -- But why not `my_string`?!
}
请注意,这必须在 Clang 上使用
-stdlib=libc++
进行测试(libstdc++ 没有此注释,并打印 std::basic_string<char>
而不是 std::string
)。
这似乎是一个 Clang 错误,我现在已在此处报告了该错误。
除非您在 TU 中的某处以特定方式使用目标类型,否则该属性不起作用。例如,在属性下方的某处添加
using X = my_basic_string<char>;
可以修复该问题(但 ... = my_string;
不起作用)。定义此类并声明 my_string
(或 my_basic_string<char>
)类型的变量也可以解决此问题。
我创建了一个宏,可以自动应用此解决方法(并且还禁用不支持它的编译器上的属性):
MY_CANONICAL_TYPEDEFS( (template <typename T> struct), my_basic_string,
(my_string, my_basic_string<char>)
(my_wstring, my_basic_string<wchar_t>)
)
#include <type_traits>
#if defined(_MSC_VER) && !defined(__clang__) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL == 1)
#error The standard-conformant MSVC preprocessor is required, enable it with `/Zc:preprocessor`.
#endif
#define MY_IDENTITY(...) __VA_ARGS__
#define MY_END(...) MY_END_(__VA_ARGS__)
#define MY_END_(...) __VA_ARGS__##_END
#define MY_CANONICAL_TYPEDEFS(type_, name_, aliases_) \
MY_IDENTITY type_ name_; \
MY_END(DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_A aliases_) \
DETAIL_MY_CANONICAL_TYPEDEFS(type_, name_, aliases_)
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_BODY(name_, target_) using name_ = target_;
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_A(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_BODY(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_B
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_B(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_BODY(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_A
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_A_END
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_B_END
#if defined(__has_attribute)
#if __has_attribute(__preferred_name__)
#define DETAIL_MY_CANONICAL_TYPEDEFS(type_, name_, aliases_) \
MY_IDENTITY type_ \
MY_END(DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_A aliases_) \
name_; \
DETAIL_MY_CANONICAL_TYPEDEFS_CLANG_WORKAROUND(aliases_)
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_BODY(name_, target_) __attribute__((__preferred_name__(name_)))
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_A(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_BODY(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_B
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_B(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_BODY(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_A
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_A_END
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_ATTR_B_END
#ifdef __clang__ // Workaround for bug: https://github.com/llvm/llvm-project/issues/106358
#define DETAIL_MY_CANONICAL_TYPEDEFS_CLANG_WORKAROUND(aliases_) \
MY_END(DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_A aliases_)
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_BODY(name_, target_) static_assert((void(std::type_identity<target_>{}), true));
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_A(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_BODY(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_B
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_B(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_BODY(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_A
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_A_END
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_TOUCH_B_END
#else // no workaround needed
#define DETAIL_MY_CANONICAL_TYPEDEFS_CLANG_WORKAROUND(aliases_)
#endif
#else // this attribute is not supported
#define DETAIL_MY_CANONICAL_TYPEDEFS(type_, name_, aliases_)
#endif
#else // no __has_attribute
#define DETAIL_MY_CANONICAL_TYPEDEFS(type_, name_, aliases_)
#endif