هل من الممكن طباعة نوع المتغير في لغة C++ القياسية؟

StackOverflow https://stackoverflow.com/questions/81870

  •  09-06-2019
  •  | 
  •  

سؤال

على سبيل المثال:

int a = 12;
cout << typeof(a) << endl;

الناتج المتوقع:

int
هل كانت مفيدة؟

المحلول

تحديث C++ 11 لسؤال قديم جدًا:طباعة نوع المتغير في C++.

الإجابة المقبولة (والجيدة) هي الاستخدام typeid(a).name(), ، أين a هو اسم متغير.

الآن في C++ 11 لدينا decltype(x), ، والتي يمكنها تحويل التعبير إلى نوع.و decltype() يأتي مع مجموعته الخاصة من القواعد المثيرة للاهتمام.على سبيل المثال decltype(a) و decltype((a)) ستكون أنواعًا مختلفة بشكل عام (ولأسباب جيدة ومفهومة بمجرد الكشف عن تلك الأسباب).

سوف موثوقة لدينا typeid(a).name() مساعدتنا في استكشاف هذا العالم الجديد الشجاع؟

لا.

لكن الأداة التي ستفعل ذلك ليست بهذا التعقيد.وهذه هي الأداة التي أستخدمها كإجابة على هذا السؤال.سأقوم بمقارنة هذه الأداة الجديدة ومقارنتها بـ typeid(a).name().وهذه الأداة الجديدة مبنية بالفعل على أعلى typeid(a).name().

المسألة الأساسية:

typeid(a).name()

يتخلص من مؤهلات السيرة الذاتية والمراجع وقيمة lvalue/rvalue.على سبيل المثال:

const int ci = 0;
std::cout << typeid(ci).name() << '\n';

بالنسبة لي المخرجات:

i

وأنا أخمن مخرجات MSVC:

int

أي.ال const ذهب.هذه ليست مشكلة QOI (جودة التنفيذ).المعيار يفرض هذا السلوك.

ما أوصي به أدناه هو:

template <typename T> std::string type_name();

والتي سيتم استخدامها مثل هذا:

const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';

وبالنسبة لي المخرجات:

int const

<disclaimer> لم أختبر هذا على MSVC. </disclaimer> لكنني أرحب بتعليقات أولئك الذين يفعلون ذلك.

الحل C++11

انا استخدم __cxa_demangle لمنصات غير MSVC على النحو الموصى به من قبل com.ipapadop في إجابته على أنواع التفكيك.لكني أثق في MSVC typeid لتفكيك الأسماء (غير مجربة).ويتم تغليف هذا الجوهر ببعض الاختبارات البسيطة التي تكتشف وتستعيد وتبلغ عن مؤهلات السيرة الذاتية والمراجع إلى نوع الإدخال.

#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

النتائج

مع هذا الحل يمكنني القيام بذلك:

int& foo_lref();
int&& foo_rref();
int foo_value();

int
main()
{
    int i = 0;
    const int ci = 0;
    std::cout << "decltype(i) is " << type_name<decltype(i)>() << '\n';
    std::cout << "decltype((i)) is " << type_name<decltype((i))>() << '\n';
    std::cout << "decltype(ci) is " << type_name<decltype(ci)>() << '\n';
    std::cout << "decltype((ci)) is " << type_name<decltype((ci))>() << '\n';
    std::cout << "decltype(static_cast<int&>(i)) is " << type_name<decltype(static_cast<int&>(i))>() << '\n';
    std::cout << "decltype(static_cast<int&&>(i)) is " << type_name<decltype(static_cast<int&&>(i))>() << '\n';
    std::cout << "decltype(static_cast<int>(i)) is " << type_name<decltype(static_cast<int>(i))>() << '\n';
    std::cout << "decltype(foo_lref()) is " << type_name<decltype(foo_lref())>() << '\n';
    std::cout << "decltype(foo_rref()) is " << type_name<decltype(foo_rref())>() << '\n';
    std::cout << "decltype(foo_value()) is " << type_name<decltype(foo_value())>() << '\n';
}

والإخراج هو:

decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast<int&>(i)) is int&
decltype(static_cast<int&&>(i)) is int&&
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int&
decltype(foo_rref()) is int&&
decltype(foo_value()) is int

لاحظ (على سبيل المثال) الفرق بين decltype(i) و decltype((i)).السابق هو نوع من تصريح ل i.هذا الأخير هو "النوع" من تعبير i.(التعبيرات لا تحتوي أبدًا على نوع مرجعي، ولكن كاتفاقية decltype يمثل تعبيرات القيمة ذات مراجع القيمة).

وبالتالي فإن هذه الأداة هي وسيلة ممتازة للتعرف عليها فقط decltype, بالإضافة إلى استكشاف وتصحيح التعليمات البرمجية الخاصة بك.

على النقيض من ذلك، إذا كنت سأبني هذا فقط typeid(a).name(), ، بدون إضافة مؤهلات أو مراجع السيرة الذاتية المفقودة، سيكون الناتج كما يلي:

decltype(i) is int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast<int&>(i)) is int
decltype(static_cast<int&&>(i)) is int
decltype(static_cast<int>(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(foo_value()) is int

أي.يتم تجريد كل مرجع ومؤهل السيرة الذاتية.

تحديث سي++14

عندما تظن أنك قد حصلت على حل لمشكلة ما، دائمًا ما يأتي شخص ما من العدم ويوضح لك طريقة أفضل بكثير.:-)

هذه الإجابة من المخيم يوضح كيفية الحصول على اسم النوع في C++ 14 في وقت الترجمة.إنه حل رائع لعدة أسباب:

  1. إنه في وقت التجميع!
  2. يمكنك جعل المترجم نفسه يقوم بالمهمة بدلاً من المكتبة (حتى std::lib).وهذا يعني نتائج أكثر دقة لأحدث ميزات اللغة (مثل lambdas).

المخيم إجابة لا يوضح كل شيء تمامًا لـ VS، وأنا أقوم بتعديل الكود الخاص به قليلاً.ولكن نظرًا لأن هذه الإجابة تحصل على الكثير من المشاهدات، فاستغرق بعض الوقت للذهاب إلى هناك والتصويت لصالح إجابته، والتي بدونها، لم يكن من الممكن أن يحدث هذا التحديث أبدًا.

#include <cstddef>
#include <stdexcept>
#include <cstring>
#include <ostream>

#ifndef _MSC_VER
#  if __cplusplus < 201103
#    define CONSTEXPR11_TN
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN
#  elif __cplusplus < 201402
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN noexcept
#  else
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN constexpr
#    define NOEXCEPT_TN noexcept
#  endif
#else  // _MSC_VER
#  if _MSC_VER < 1900
#    define CONSTEXPR11_TN
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN
#  elif _MSC_VER < 2000
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN
#    define NOEXCEPT_TN noexcept
#  else
#    define CONSTEXPR11_TN constexpr
#    define CONSTEXPR14_TN constexpr
#    define NOEXCEPT_TN noexcept
#  endif
#endif  // _MSC_VER

class static_string
{
    const char* const p_;
    const std::size_t sz_;

public:
    typedef const char* const_iterator;

    template <std::size_t N>
    CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN
        : p_(a)
        , sz_(N-1)
        {}

    CONSTEXPR11_TN static_string(const char* p, std::size_t N) NOEXCEPT_TN
        : p_(p)
        , sz_(N)
        {}

    CONSTEXPR11_TN const char* data() const NOEXCEPT_TN {return p_;}
    CONSTEXPR11_TN std::size_t size() const NOEXCEPT_TN {return sz_;}

    CONSTEXPR11_TN const_iterator begin() const NOEXCEPT_TN {return p_;}
    CONSTEXPR11_TN const_iterator end()   const NOEXCEPT_TN {return p_ + sz_;}

    CONSTEXPR11_TN char operator[](std::size_t n) const
    {
        return n < sz_ ? p_[n] : throw std::out_of_range("static_string");
    }
};

inline
std::ostream&
operator<<(std::ostream& os, static_string const& s)
{
    return os.write(s.data(), s.size());
}

template <class T>
CONSTEXPR14_TN
static_string
type_name()
{
#ifdef __clang__
    static_string p = __PRETTY_FUNCTION__;
    return static_string(p.data() + 31, p.size() - 31 - 1);
#elif defined(__GNUC__)
    static_string p = __PRETTY_FUNCTION__;
#  if __cplusplus < 201402
    return static_string(p.data() + 36, p.size() - 36 - 1);
#  else
    return static_string(p.data() + 46, p.size() - 46 - 1);
#  endif
#elif defined(_MSC_VER)
    static_string p = __FUNCSIG__;
    return static_string(p.data() + 38, p.size() - 38 - 7);
#endif
}

سيتم التراجع تلقائيًا عن هذا الرمز في constexpr إذا كنت لا تزال عالقًا في لغة C++ 11 القديمة.وإذا كنت ترسم على جدار الكهف باستخدام C++98/03، فإن noexcept يتم التضحية به أيضًا.

تحديث سي++17

في التعليقات أدناه ليبيرتا ويشير إلى أن الجديد std::string_view يمكن استبدال static_string:

template <class T>
constexpr
std::string_view
type_name()
{
    using namespace std;
#ifdef __clang__
    string_view p = __PRETTY_FUNCTION__;
    return string_view(p.data() + 34, p.size() - 34 - 1);
#elif defined(__GNUC__)
    string_view p = __PRETTY_FUNCTION__;
#  if __cplusplus < 201402
    return string_view(p.data() + 36, p.size() - 36 - 1);
#  else
    return string_view(p.data() + 49, p.find(';', 49) - 49);
#  endif
#elif defined(_MSC_VER)
    string_view p = __FUNCSIG__;
    return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}

لقد قمت بتحديث ثوابت VS بفضل العمل البوليسي الرائع الذي قام به Jive Dadson في التعليقات أدناه.

نصائح أخرى

يحاول:

#include <typeinfo>

// …
std::cout << typeid(a).name() << '\n';

قد يتعين عليك تنشيط RTTI في خيارات برنامج التحويل البرمجي الخاص بك حتى يعمل هذا.بالإضافة إلى ذلك، يعتمد إخراج هذا على المترجم.قد يكون اسمًا من النوع الخام أو رمزًا لتشويه الاسم أو أي شيء بينهما.

قبيح جدًا ولكنه يفي بالغرض إذا كنت تريد فقط تجميع معلومات الوقت (على سبيل المثال.للتصحيح):

auto testVar = std::make_tuple(1, 1.0, "abc");
decltype(testVar)::foo = 1;

عائدات:

Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:5:19: error: 'dummy_error' is not a member of 'std::tuple<int, double, const char*>'

لا تنسى أن تشمل <typeinfo>

أعتقد أن ما تشير إليه هو تحديد نوع وقت التشغيل.يمكنك تحقيق ما سبق عن طريق القيام به.

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
  int i;
  cout << typeid(i).name();
  return 0;
}

لاحظ أن الأسماء التي تم إنشاؤها بواسطة ميزة RTTI لـ C++ هي لا محمول.على سبيل المثال، الطبقة

MyNamespace::CMyContainer<int, test_MyNamespace::CMyObject>

سيكون لها الأسماء التالية:

// MSVC 2003:
class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject]
// G++ 4.2:
N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE

لذلك لا يمكنك استخدام هذه المعلومات للتسلسل.لكن لا يزال من الممكن استخدام الخاصية typeid(a).name() لأغراض التسجيل/تصحيح الأخطاء

يمكنك استخدام القوالب.

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(float&) { return "float"; }

في المثال أعلاه، عندما لا يكون النوع مطابقًا، سيتم طباعة "غير معروف".

كما ذكر، typeid().name() قد يعود اسم مشوه.في دول مجلس التعاون الخليجي (وبعض المترجمين الآخرين) يمكنك حل هذه المشكلة باستخدام الكود التالي:

#include <cxxabi.h>
#include <iostream>
#include <typeinfo>
#include <cstdlib>

namespace some_namespace { namespace another_namespace {

  class my_class { };

} }

int main() {
  typedef some_namespace::another_namespace::my_class my_type;
  // mangled
  std::cout << typeid(my_type).name() << std::endl;

  // unmangled
  int status = 0;
  char* demangled = abi::__cxa_demangle(typeid(my_type).name(), 0, 0, &status);

  switch (status) {
    case -1: {
      // could not allocate memory
      std::cout << "Could not allocate memory" << std::endl;
      return -1;
    } break;
    case -2: {
      // invalid name under the C++ ABI mangling rules
      std::cout << "Invalid name" << std::endl;
      return -1;
    } break;
    case -3: {
      // invalid argument
      std::cout << "Invalid argument to demangle()" << std::endl;
      return -1;
    } break;
 }
 std::cout << demangled << std::endl;

 free(demangled);

 return 0;

}

يمكنك استخدام فئة السمات لهذا الغرض.شيء مثل:

#include <iostream>
using namespace std;

template <typename T> class type_name {
public:
    static const char *name;
};

#define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
#define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)

DECLARE_TYPE_NAME(int);

int main()
{
    int a = 12;
    cout << GET_TYPE_NAME(a) << endl;
}

ال DECLARE_TYPE_NAME تحديد موجود لجعل حياتك أسهل في الإعلان عن فئة السمات هذه لجميع الأنواع التي تتوقع أن تحتاجها.

قد يكون هذا أكثر فائدة من الحلول التي تنطوي على typeid لأنك تستطيع التحكم في الإخراج.على سبيل المثال، باستخدام typeid ل long long على المترجم الخاص بي يعطي "x".

في C++ 11، لدينا نوع decltype.لا توجد طريقة في لغة c++ القياسية لعرض النوع الدقيق للمتغير المعلن باستخدام decltype.يمكننا استخدام مؤشر typeindex دفعة أي type_id_with_cvr (cvr يرمز إلى const وvolatile وreference) لطباعة النوع كما هو موضح أدناه.

#include <iostream>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr;

int main() {
  int i = 0;
  const int ci = 0;
  cout << "decltype(i) is " << type_id_with_cvr<decltype(i)>().pretty_name() << '\n';
  cout << "decltype((i)) is " << type_id_with_cvr<decltype((i))>().pretty_name() << '\n';
  cout << "decltype(ci) is " << type_id_with_cvr<decltype(ci)>().pretty_name() << '\n';
  cout << "decltype((ci)) is " << type_id_with_cvr<decltype((ci))>().pretty_name() << '\n';
  cout << "decltype(std::move(i)) is " << type_id_with_cvr<decltype(std::move(i))>().pretty_name() << '\n';
  cout << "decltype(std::static_cast<int&&>(i)) is " << type_id_with_cvr<decltype(static_cast<int&&>(i))>().pretty_name() << '\n';
  return 0;
}

ربما تكون الإجابات الأخرى التي تتضمن RTTI (typeid) هي ما تريده، طالما:

  • يمكنك تحمل تكاليف الذاكرة (والتي يمكن أن تكون كبيرة مع بعض المترجمين)
  • أسماء الفئات التي يُرجعها المترجم مفيدة

البديل (على غرار إجابة جريج هيوجيل) هو بناء جدول زمني للسمات.

template <typename T> struct type_as_string;

// declare your Wibble type (probably with definition of Wibble)
template <>
struct type_as_string<Wibble>
{
    static const char* const value = "Wibble";
};

انتبه إلى أنه إذا قمت بلف الإعلانات في ماكرو، فسوف تواجه مشكلة في إعلان أسماء أنواع القوالب التي تأخذ أكثر من معلمة واحدة (على سبيل المثال.std::map)، بسبب الفاصلة.

للوصول إلى اسم نوع المتغير، كل ما تحتاجه هو

template <typename T>
const char* get_type_as_string(const T&)
{
    return type_as_string<T>::value;
}

حل أكثر عمومية دون التحميل الزائد للوظيفة من الحل السابق:

template<typename T>
std::string TypeOf(T){
    std::string Type="unknown";
    if(std::is_same<T,int>::value) Type="int";
    if(std::is_same<T,std::string>::value) Type="String";
    if(std::is_same<T,MyClass>::value) Type="MyClass";

    return Type;}

هنا MyClass هي فئة محددة من قبل المستخدم.يمكن إضافة المزيد من الشروط هنا أيضًا.

مثال:

#include <iostream>



class MyClass{};


template<typename T>
std::string TypeOf(T){
    std::string Type="unknown";
    if(std::is_same<T,int>::value) Type="int";
    if(std::is_same<T,std::string>::value) Type="String";
    if(std::is_same<T,MyClass>::value) Type="MyClass";
    return Type;}


int main(){;
    int a=0;
    std::string s="";
    MyClass my;
    std::cout<<TypeOf(a)<<std::endl;
    std::cout<<TypeOf(s)<<std::endl;
    std::cout<<TypeOf(my)<<std::endl;

    return 0;}

انتاج:

int
String
MyClass

تعجبني طريقة Nick، وقد يكون النموذج الكامل هو هذا (لجميع أنواع البيانات الأساسية):

template <typename T> const char* typeof(T&) { return "unknown"; }    // default
template<> const char* typeof(int&) { return "int"; }
template<> const char* typeof(short&) { return "short"; }
template<> const char* typeof(long&) { return "long"; }
template<> const char* typeof(unsigned&) { return "unsigned"; }
template<> const char* typeof(unsigned short&) { return "unsigned short"; }
template<> const char* typeof(unsigned long&) { return "unsigned long"; }
template<> const char* typeof(float&) { return "float"; }
template<> const char* typeof(double&) { return "double"; }
template<> const char* typeof(long double&) { return "long double"; }
template<> const char* typeof(std::string&) { return "String"; }
template<> const char* typeof(char&) { return "char"; }
template<> const char* typeof(signed char&) { return "signed char"; }
template<> const char* typeof(unsigned char&) { return "unsigned char"; }
template<> const char* typeof(char*&) { return "char*"; }
template<> const char* typeof(signed char*&) { return "signed char*"; }
template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }

يمكنك أيضًا استخدام c++filt مع الخيار -t (النوع) لفك اسم النوع:

#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

int main() {
  auto x = 1;
  string my_type = typeid(x).name();
  system(("echo " + my_type + " | c++filt -t").c_str());
  return 0;
}

تم اختباره على لينكس فقط.

بينما أتحدى، قررت أن أختبر إلى أي مدى يمكن للمرء أن يصل باستخدام خداع القوالب المستقلة عن النظام الأساسي (كما نأمل).

يتم تجميع الأسماء بالكامل في وقت التجميع.(والذي يعني typeid(T).name() لا يمكن استخدامها، وبالتالي يتعين عليك تقديم أسماء للأنواع غير المركبة بشكل صريح.وإلا فسيتم عرض العناصر النائبة بدلاً من ذلك.)

مثال على الاستخدام:

TYPE_NAME(int)
TYPE_NAME(void)
// You probably should list all primitive types here.

TYPE_NAME(std::string)

int main()
{
    // A simple case
    std::cout << type_name<void(*)(int)> << '\n';
    // -> `void (*)(int)`

    // Ugly mess case
    // Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers.
    std::cout << type_name<void (std::string::*(int[3],const int, void (*)(std::string)))(volatile int*const*)> << '\n';
    // -> `void (std::string::*(int *,int,void (*)(std::string)))(volatile int *const*)`

    // A case with undefined types
    //  If a type wasn't TYPE_NAME'd, it's replaced by a placeholder, one of `class?`, `union?`, `enum?` or `??`.
    std::cout << type_name<std::ostream (*)(int, short)> << '\n';
    // -> `class? (*)(int,??)`
    // With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`.
}

شفرة:

#include <type_traits>
#include <utility>

static constexpr std::size_t max_str_lit_len = 256;

template <std::size_t I, std::size_t N> constexpr char sl_at(const char (&str)[N])
{
    if constexpr(I < N)
        return str[I];
    else
        return '\0';
}

constexpr std::size_t sl_len(const char *str)
{
    for (std::size_t i = 0; i < max_str_lit_len; i++)
        if (str[i] == '\0')
            return i;
    return 0;
}

template <char ...C> struct str_lit
{
    static constexpr char value[] {C..., '\0'};
    static constexpr int size = sl_len(value);

    template <typename F, typename ...P> struct concat_impl {using type = typename concat_impl<F>::type::template concat_impl<P...>::type;};
    template <char ...CC> struct concat_impl<str_lit<CC...>> {using type = str_lit<C..., CC...>;};
    template <typename ...P> using concat = typename concat_impl<P...>::type;
};

template <typename, const char *> struct trim_str_lit_impl;
template <std::size_t ...I, const char *S> struct trim_str_lit_impl<std::index_sequence<I...>, S>
{
    using type = str_lit<S[I]...>;
};
template <std::size_t N, const char *S> using trim_str_lit = typename trim_str_lit_impl<std::make_index_sequence<N>, S>::type;

#define STR_LIT(str) ::trim_str_lit<::sl_len(str), ::str_lit<STR_TO_VA(str)>::value>
#define STR_TO_VA(str) STR_TO_VA_16(str,0),STR_TO_VA_16(str,16),STR_TO_VA_16(str,32),STR_TO_VA_16(str,48)
#define STR_TO_VA_16(str,off) STR_TO_VA_4(str,0+off),STR_TO_VA_4(str,4+off),STR_TO_VA_4(str,8+off),STR_TO_VA_4(str,12+off)
#define STR_TO_VA_4(str,off) ::sl_at<off+0>(str),::sl_at<off+1>(str),::sl_at<off+2>(str),::sl_at<off+3>(str)

template <char ...C> constexpr str_lit<C...> make_str_lit(str_lit<C...>) {return {};}
template <std::size_t N> constexpr auto make_str_lit(const char (&str)[N])
{
    return trim_str_lit<sl_len((const char (&)[N])str), str>{};
}

template <std::size_t A, std::size_t B> struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow<A,B-1>::value;};
template <std::size_t A> struct cexpr_pow<A,0> {static constexpr std::size_t value = 1;};
template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_str_lit_impl;
template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_str_lit_impl<N, X, std::index_sequence<Seq...>>
{
    static constexpr auto func()
    {
        if constexpr (N >= cexpr_pow<10,X>::value)
            return num_to_str_lit_impl<N, X+1>::func();
        else
            return str_lit<(N / cexpr_pow<10,X-1-Seq>::value % 10 + '0')...>{};
    }
};
template <std::size_t N> using num_to_str_lit = decltype(num_to_str_lit_impl<N,1>::func());


using spa = str_lit<' '>;
using lpa = str_lit<'('>;
using rpa = str_lit<')'>;
using lbr = str_lit<'['>;
using rbr = str_lit<']'>;
using ast = str_lit<'*'>;
using amp = str_lit<'&'>;
using con = str_lit<'c','o','n','s','t'>;
using vol = str_lit<'v','o','l','a','t','i','l','e'>;
using con_vol = con::concat<spa, vol>;
using nsp = str_lit<':',':'>;
using com = str_lit<','>;
using unk = str_lit<'?','?'>;

using c_cla = str_lit<'c','l','a','s','s','?'>;
using c_uni = str_lit<'u','n','i','o','n','?'>;
using c_enu = str_lit<'e','n','u','m','?'>;

template <typename T> inline constexpr bool ptr_or_ref = std::is_pointer_v<T> || std::is_reference_v<T> || std::is_member_pointer_v<T>;
template <typename T> inline constexpr bool func_or_arr = std::is_function_v<T> || std::is_array_v<T>;

template <typename T> struct primitive_type_name {using value = unk;};

template <typename T, typename = std::enable_if_t<std::is_class_v<T>>> using enable_if_class = T;
template <typename T, typename = std::enable_if_t<std::is_union_v<T>>> using enable_if_union = T;
template <typename T, typename = std::enable_if_t<std::is_enum_v <T>>> using enable_if_enum  = T;
template <typename T> struct primitive_type_name<enable_if_class<T>> {using value = c_cla;};
template <typename T> struct primitive_type_name<enable_if_union<T>> {using value = c_uni;};
template <typename T> struct primitive_type_name<enable_if_enum <T>> {using value = c_enu;};

template <typename T> struct type_name_impl;

template <typename T> using type_name_lit = std::conditional_t<std::is_same_v<typename primitive_type_name<T>::value::template concat<spa>,
                                                                               typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>,
                                            typename primitive_type_name<T>::value,
                                            typename type_name_impl<T>::l::template concat<typename type_name_impl<T>::r>>;
template <typename T> inline constexpr const char *type_name = type_name_lit<T>::value;

template <typename T, typename = std::enable_if_t<!std::is_const_v<T> && !std::is_volatile_v<T>>> using enable_if_no_cv = T;

template <typename T> struct type_name_impl
{
    using l = typename primitive_type_name<T>::value::template concat<spa>;
    using r = str_lit<>;
};
template <typename T> struct type_name_impl<const T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<con>,
                                 con::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<volatile T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<vol>,
                                 vol::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<const volatile T>
{
    using new_T_l = std::conditional_t<type_name_impl<T>::l::size && !ptr_or_ref<T>,
                                       spa::concat<typename type_name_impl<T>::l>,
                                       typename type_name_impl<T>::l>;
    using l = std::conditional_t<ptr_or_ref<T>,
                                 typename new_T_l::template concat<con_vol>,
                                 con_vol::concat<new_T_l>>;
    using r = typename type_name_impl<T>::r;
};
template <typename T> struct type_name_impl<T *>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, ast>,
                                 typename type_name_impl<T>::l::template concat<     ast>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, amp>,
                                 typename type_name_impl<T>::l::template concat<     amp>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T &&>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, amp, amp>,
                                 typename type_name_impl<T>::l::template concat<     amp, amp>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T, typename C> struct type_name_impl<T C::*>
{
    using l = std::conditional_t<func_or_arr<T>,
                                 typename type_name_impl<T>::l::template concat<lpa, type_name_lit<C>, nsp, ast>,
                                 typename type_name_impl<T>::l::template concat<     type_name_lit<C>, nsp, ast>>;
    using r = std::conditional_t<func_or_arr<T>,
                                 rpa::concat<typename type_name_impl<T>::r>,
                                             typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<enable_if_no_cv<T[]>>
{
    using l = typename type_name_impl<T>::l;
    using r = lbr::concat<rbr, typename type_name_impl<T>::r>;
};
template <typename T, std::size_t N> struct type_name_impl<enable_if_no_cv<T[N]>>
{
    using l = typename type_name_impl<T>::l;
    using r = lbr::concat<num_to_str_lit<N>, rbr, typename type_name_impl<T>::r>;
};
template <typename T> struct type_name_impl<T()>
{
    using l = typename type_name_impl<T>::l;
    using r = lpa::concat<rpa, typename type_name_impl<T>::r>;
};
template <typename T, typename P1, typename ...P> struct type_name_impl<T(P1, P...)>
{
    using l = typename type_name_impl<T>::l;
    using r = lpa::concat<type_name_lit<P1>,
                          com::concat<type_name_lit<P>>..., rpa, typename type_name_impl<T>::r>;
};

#define TYPE_NAME(t) template <> struct primitive_type_name<t> {using value = STR_LIT(#t);};
#include <iostream>
#include <typeinfo>
using namespace std;
#define show_type_name(_t) \
    system(("echo " + string(typeid(_t).name()) + " | c++filt -t").c_str())

int main() {
    auto a = {"one", "two", "three"};
    cout << "Type of a: " << typeid(a).name() << endl;
    cout << "Real type of a:\n";
    show_type_name(a);
    for (auto s : a) {
        if (string(s) == "one") {
            cout << "Type of s: " << typeid(s).name() << endl;
            cout << "Real type of s:\n";
            show_type_name(s);
        }
        cout << s << endl;
    }

    int i = 5;
    cout << "Type of i: " << typeid(i).name() << endl;
    cout << "Real type of i:\n";
    show_type_name(i);
    return 0;
}

انتاج:

Type of a: St16initializer_listIPKcE
Real type of a:
std::initializer_list<char const*>
Type of s: PKc
Real type of s:
char const*
one
two
three
Type of i: i
Real type of i:
int

كما أوضح سكوت مايرز في كتاب C++ الفعال الحديث،

يدعو ل std::type_info::name لا نضمن إرجاع أي شيء معقول.

الحل الأفضل هو السماح للمترجم بإنشاء رسالة خطأ أثناء خصم النوع، على سبيل المثال،

template<typename T>
class TD;

int main(){
    const int theAnswer = 32;
    auto x = theAnswer;
    auto y = &theAnswer;
    TD<decltype(x)> xType;
    TD<decltype(y)> yType;
    return 0;
}

ستكون النتيجة شيء من هذا القبيل، اعتمادا على المترجمين،

test4.cpp:10:21: error: aggregate ‘TD<int> xType’ has incomplete type and cannot be defined TD<decltype(x)> xType;

test4.cpp:11:21: error: aggregate ‘TD<const int *> yType’ has incomplete type and cannot be defined TD<decltype(y)> yType;

ومن هنا نتعرف على ذلك xالنوع هو int, yالنوع هو const int*

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top