Search code examples
c++attributesclang

How do I use Clang's attribute `preferred_name`?


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].


Solution

  • 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):

    run on gcc.godbolt.org

    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