entt icon indicating copy to clipboard operation
entt copied to clipboard

inconsistent type_ids between compilers when using modules

Open hfn92 opened this issue 5 months ago • 1 comments

This might be a bit premature since modules are still quite experimental. But when using entt::type_id<> on a type exported from a module stripped_type_name will create different names depending on the compiler.

  • gcc(15) will create: "type@module",
  • whereas clang(21) will create "type"

This creates a inconsistency when trying to use entt::resolve("type"_hs).

Of course this can be circumvented by specializing entt::type_name<>. But it should probably still produce the same type name.

I not sure whether gcc or clang is correct here (or what msvc does).

I fixed this locally for me by changing stripped_type_name

template<typename Type>
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
#if defined ENTT_PRETTY_FUNCTION
    std::string_view pretty_function{static_cast<const char *>(ENTT_PRETTY_FUNCTION)};
    auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
    auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
    auto at = value.find_first_of('@');
    if (at != std::string_view::npos)
    {
      value = value.substr(0, at);
    }
    return value;
#else
    return std::string_view{""};
#endif
}

hfn92 avatar Jun 01 '25 06:06 hfn92

This already happens without having to bother with modules. What different compilers generate is not guaranteed to be the same, since it is not dictated by the standard. To be fair, even different versions of the same compiler can return different strings (and yeah, it happened in the past). See __PRETTY_FUNCTION__ vs __FUNCSIG__ online. Some compilers do not even offer a name, just to make things easier. 🙂 Built-in names are a best effort by EnTT. They are only guaranteed to work properly if you compile your software with the same compiler and version. In all other cases, you can (and probably should) specialize type_name. That is the reason why it's sfinae-friendly too. For example:

template<typename Type>
struct entt::type_name<Type> final {
    [[nodiscard]] static constexpr std::string_view value() noexcept {
        return std::string_view{Type::my_custom_name};
    }

    [[nodiscard]] constexpr operator std::string_view() const noexcept {
        return value();
    }
};

// ...

struct my_component {
    static inline const char *my_custom_name = "my_component";
    // ...
};

skypjack avatar Jun 02 '25 15:06 skypjack