Clang has a non-standard attribute preferred_name
that is used e.g. in libc++ to spell std::basic_string<char>
as std::string
(which is more user-friendly).
I'm trying to use it for my own types, but it doesn't seem to work: 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`?!
}
Note that this must be tested on Clang, with -stdlib=libc++
(libstdc++ doesn't have this annotation, and prints std::basic_string<char>
instead of std::string
).
Am I getting the syntax wrong? I believe I'm doing the same thing as libc++ does: [1], [2].
This appears to be a Clang bug, which I've now reported here.
The attribute doesn't work unless you use the target type in a specific way somewhere in the TU. For example, adding using X = my_basic_string<char>;
somewhere below the attribute fixes it (... = my_string;
doesn't work though). Defining this class and declaring a variable of type my_string
(or my_basic_string<char>
) also fixes it.
I've created a macro that automatically applies this workaround (and also disables the attribute on compilers that don't support it):
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>
#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_, ...) using name_ = __VA_ARGS__;
// Adding `MY_IDENTITY()` here to keep the legacy MSVC preprocessor happy.
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_A(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_BODY MY_IDENTITY()(__VA_ARGS__) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_B
#define DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_B(...) DETAIL_MY_CANONICAL_TYPEDEFS_LOOP_USING_BODY MY_IDENTITY()(__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_, ...) __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_, ...) static_assert((void(std::type_identity<__VA_ARGS__>{}), 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