Возможно ли напечатать тип переменной в стандартном C ++?
Например:
int a = 12; cout << typeof(a) << endl;
Ожидаемый результат:
int
- Нужно ли назначать строку переменной перед ее сопоставлением с другим?
- Создание нескольких нумерованных переменных на основе int
- BASH Перебор переменных из файла конфигурации
- В чем разница между атрибутами classа и экземпляра?
- Как проверить, установлена ли переменная в Bash?
- объявить глобальную собственность в QML для других файлов QML
- Когда статические переменные уровня функции получаются / инициализированы?
- C ++ int с предшествующим 0 изменяет всю ценность
- Будет ли использование «var» влиять на производительность?
- Определить имена таблиц и столбцов в качестве аргументов в функции plpgsql?
- Передать переменные jquery между iframe и parent
- Какая польза от частной статической переменной в Java?
- Есть ли что-нибудь вроде deal () для обычных массивов MATLAB?
Обновление 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()
.
Основной вопрос:
typeid(a).name()
выбрасывает cv-квалификаторы, ссылки и lvalue / rvalue-ness. Например:
const int ci = 0; std::cout << typeid(ci).name() << '\n';
Для меня выходы:
i
и я предполагаю на выходах MSVC:
int
Т.е. const
ушел. Это не вопрос QOI (Quality of Implementation). Стандарт предусматривает такое поведение.
Ниже я рекомендую следующее:
template std::string type_name();
который будет использоваться следующим образом:
const int ci = 0; std::cout << type_name() << '\n';
и для меня выходы:
int const
Я не тестировал это на MSVC. Но я приветствую отзывы от тех, кто это делает.
Решение C ++ 11
Я использую __cxa_demangle
для платформ без MSVC, как рекомендую ipapadop в его ответе на типы demangle. Но на MSVC я typeid
чтобы demangle имена (untested). И это kernel обернуто вокруг простого тестирования, которое обнаруживает, восстанавливает и сообщает cv-квалификаторы и ссылки на тип ввода.
#include #include #ifndef _MSC_VER # include #endif #include #include #include template std::string type_name() { typedef typename std::remove_reference::type TR; std::unique_ptr 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::value) r += " const"; if (std::is_volatile ::value) r += " volatile"; if (std::is_lvalue_reference::value) r += "&"; else if (std::is_rvalue_reference ::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() << '\n'; std::cout << "decltype((i)) is " << type_name() << '\n'; std::cout << "decltype(ci) is " << type_name() << '\n'; std::cout << "decltype((ci)) is " << type_name() << '\n'; std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n'; std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n'; std::cout << "decltype(static_cast(i)) is " << type_name(i))>() << '\n'; std::cout << "decltype(foo_lref()) is " << type_name() << '\n'; std::cout << "decltype(foo_rref()) is " << type_name() << '\n'; std::cout << "decltype(foo_value()) is " << type_name() << '\n'; }
и выход:
decltype(i) is int decltype((i)) is int& decltype(ci) is int const decltype((ci)) is int const& decltype(static_cast(i)) is int& decltype(static_cast(i)) is int&& decltype(static_cast(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
представляет выражения lvalue с ссылками lvalue).
Таким образом, этот инструмент - отличное транспортное средство, чтобы узнать о decltype
, помимо изучения и отладки вашего собственного кода.
В отличие от этого, если бы я должен был построить это только на typeid(a).name()
, не добавляя обратно потерянные cv-квалификаторы или ссылки, результатом будет:
decltype(i) is int decltype((i)) is int decltype(ci) is int decltype((ci)) is int decltype(static_cast(i)) is int decltype(static_cast(i)) is int decltype(static_cast(i)) is int decltype(foo_lref()) is int decltype(foo_rref()) is int decltype(foo_value()) is int
Т.е. каждый ссылочный и cv-определитель удаляется.
Обновление C ++ 14
Просто, когда вы думаете, что у вас есть решение проблемы с гвоздями, кто-то всегда выходит из ниоткуда и показывает вам гораздо лучший способ. 🙂
Этот ответ от Jamboree показывает, как получить имя типа в C ++ 14 во время компиляции. Это блестящее решение по нескольким причинам:
- Это во время компиляции!
- Вы сами получаете компилятор для выполнения задания вместо библиотеки (даже std :: lib). Это означает более точные результаты для последних языковых функций (например, lambdas).
Ответ Джамбори не совсем исчерпывает VS, и я немного подстраиваю его код. Но так как этот ответ получает много взглядов, уделите некоторое время, чтобы пойти туда и перенести его ответ, без которого это обновление никогда бы не произошло.
#include #include #include #include #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 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 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
на constexpr
если вы все еще застряли в древнем C ++ 11. И если вы рисуете на стене пещеры с C ++ 98/03, то noexcept
жертвует.
Обновление C ++ 17
В комментариях ниже Lyberta указывает, что новый std::string_view
может заменить static_string
:
template 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 // … std::cout << typeid(a).name() << '\n';
Возможно, вам придется активировать RTTI в настройках вашего компилятора, чтобы это работало. Кроме того, вывод этого зависит от компилятора. Это может быть имя типа raw или символ переключения имени или что-то среднее между ними.
Очень уродливый, но трюк, если вам нужна информация времени компиляции (например, для отладки):
auto testVar = std::make_tuple(1, 1.0, "abc"); static_assert(decltype(testVar)::dummy_error, "DUMP MY TYPE" );
Возвращает:
Compilation finished with errors: source.cpp: In function 'int main()': source.cpp:5:19: error: 'dummy_error' is not a member of 'std::tuple'
Не забудьте включить
Я считаю, что вы имеете в виду идентификацию типа времени выполнения. Вы можете добиться этого, выполнив это.
#include #include using namespace std; int main() { int i; cout << typeid(i).name(); return 0; }
Обратите внимание, что имена, сгенерированные функцией RTTI на C ++, не являются переносимыми. Например, class
MyNamespace::CMyContainer
будут иметь следующие названия:
// MSVC 2003: class MyNamespace::CMyContainer[int,class test_MyNamespace::CMyObject] // G++ 4.2: N8MyNamespace8CMyContainerIiN13test_MyNamespace9CMyObjectEEE
Поэтому вы не можете использовать эту информацию для сериализации. Но все же свойство typeid (a) .name () все еще может использоваться для целей журнала / отладки
Вы можете использовать шаблоны.
template const char* typeof(T&) { return "unknown"; } // default template<> const char* typeof(int&) { return "int"; } template<> const char* typeof(float&) { return "float"; }
В приведенном выше примере, когда тип не сопоставляется, он будет печатать «unknown».
Как уже упоминалось, typeid().name()
может возвращать искаженное имя. В GCC (и некоторых других компиляторах) вы можете обойти его со следующим кодом:
#include #include #include #include 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;
}
Для этого вы можете использовать class признаков. Что-то вроде:
#include using namespace std; template class type_name { public: static const char *name; }; #define DECLARE_TYPE_NAME(x) template<> const char *type_name::name = #x; #define GET_TYPE_NAME(x) (type_name::name) DECLARE_TYPE_NAME(int); int main() { int a = 12; cout << GET_TYPE_NAME(a) << endl; }
Определяется DECLARE_TYPE_NAME
чтобы упростить вашу жизнь при объявлении этого classа признаков для всех типов, которые вам понадобятся.
Это может быть более полезно, чем решения, связанные с typeid
потому что вы можете контролировать вывод. Например, использование typeid
для long long
на моем компиляторе дает «x».
Другие ответы, связанные с RTTI (typeid), вероятно, вы хотите, если:
- вы можете позволить себе накладные расходы памяти (что может быть значительным для некоторых компиляторов)
- имена classов, возвращаемые компилятором, полезны
Альтернатива, (как и ответ Грега Хьюджилла), заключается в построении таблицы характеристик времени компиляции.
template struct type_as_string; // declare your Wibble type (probably with definition of Wibble) template <> struct type_as_string { static const char* const value = "Wibble"; };
Имейте в виду, что если вы завернете объявления в макросе, вам будет сложно объявить имена типов шаблонов, принимающих более одного параметра (например, std :: map) из-за запятой.
Чтобы получить доступ к имени типа переменной, все, что вам нужно, это
template const char* get_type_as_string(const T&) { return type_as_string::value; }
В C ++ 11 у нас есть decltype. В стандартном c ++ нет способа отображения точного типа переменной, объявленной с помощью decltype. Мы можем использовать boost typeindex, т. type_id_with_cvr
(cvr означает const, volatile, reference) для типа печати, как показано ниже.
#include #include 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().pretty_name() << '\n'; cout << "decltype((i)) is " << type_id_with_cvr().pretty_name() << '\n'; cout << "decltype(ci) is " << type_id_with_cvr().pretty_name() << '\n'; cout << "decltype((ci)) is " << type_id_with_cvr().pretty_name() << '\n'; cout << "decltype(std::move(i)) is " << type_id_with_cvr().pretty_name() << '\n'; cout << "decltype(std::static_cast(i)) is " << type_id_with_cvr(i))>().pretty_name() << '\n'; return 0; }
Более общее решение без перегрузки функций, чем предыдущее:
template std::string TypeOf(T){ std::string Type="unknown"; if(std::is_same::value) Type="int"; if(std::is_same::value) Type="String"; if(std::is_same::value) Type="MyClass"; return Type;}
Здесь MyClass – это определенный пользователем class. Здесь также могут быть добавлены дополнительные условия.
Пример:
#include class MyClass{}; template std::string TypeOf(T){ std::string Type="unknown"; if(std::is_same::value) Type="int"; if(std::is_same::value) Type="String"; if(std::is_same::value) Type="MyClass"; return Type;} int main(){; int a=0; std::string s=""; MyClass my; std::cout<
Вывод:
int String MyClass
Мне нравится метод Ника, полная форма может быть такой (для всех основных типов данных):
template 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 ++ filter с опцией -t (type), чтобы развернуть имя типа:
#include #include #include 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; }
Проверено только на Linux.
Поскольку я бросаю вызов, я решил проверить, как далеко можно идти с независимым от платформы (надеюсь) шаблоном.
Имена полностью собраны во время компиляции. (Это означает, что 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 << '\n'; // -> `void (*)(int)` // Ugly mess case // Note that compiler removes cv-qualifiers from parameters and replaces arrays with pointers. std::cout << type_name << '\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 << '\n'; // -> `class? (*)(int,??)` // With appropriate TYPE_NAME's, the output would be `std::string (*)(int,short)`. }
Код:
#include #include static constexpr std::size_t max_str_lit_len = 256; template 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 struct str_lit { static constexpr char value[] {C..., '\0'}; static constexpr int size = sl_len(value); template struct concat_impl {using type = typename concat_impl::type::template concat_impl::type;}; template struct concat_impl> {using type = str_lit;}; template using concat = typename concat_impl::type; }; template struct trim_str_lit_impl; template struct trim_str_lit_impl, S> { using type = str_lit; }; template using trim_str_lit = typename trim_str_lit_impl, S>::type; #define STR_LIT(str) ::trim_str_lit<::sl_len(str), ::str_lit::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(str),::sl_at(str),::sl_at(str),::sl_at(str) template constexpr str_lit make_str_lit(str_lit) {return {};} template constexpr auto make_str_lit(const char (&str)[N]) { return trim_str_lit{}; } template struct cexpr_pow {static constexpr std::size_t value = A * cexpr_pow::value;}; template struct cexpr_pow {static constexpr std::size_t value = 1;}; template > struct num_to_str_lit_impl; template struct num_to_str_lit_impl> { static constexpr auto func() { if constexpr (N >= cexpr_pow<10,X>::value) return num_to_str_lit_impl::func(); else return str_lit<(N / cexpr_pow<10,X-1-Seq>::value % 10 + '0')...>{}; } }; template using num_to_str_lit = decltype(num_to_str_lit_impl::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; 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 inline constexpr bool ptr_or_ref = std::is_pointer_v || std::is_reference_v || std::is_member_pointer_v ; template inline constexpr bool func_or_arr = std::is_function_v || std::is_array_v ; template struct primitive_type_name {using value = unk;}; template >> using enable_if_class = T; template >> using enable_if_union = T; template >> using enable_if_enum = T; template struct primitive_type_name> {using value = c_cla;}; template struct primitive_type_name> {using value = c_uni;}; template struct primitive_type_name> {using value = c_enu;}; template struct type_name_impl; template using type_name_lit = std::conditional_t::value::template concat, typename type_name_impl::l::template concat::r>>, typename primitive_type_name::value, typename type_name_impl ::l::template concat::r>>; template inline constexpr const char *type_name = type_name_lit::value; template && !std::is_volatile_v>> using enable_if_no_cv = T; template struct type_name_impl { using l = typename primitive_type_name::value::template concat; using r = str_lit<>; }; template struct type_name_impl { using new_T_l = std::conditional_t::l::size && !ptr_or_ref , spa::concat::l>, typename type_name_impl::l>; using l = std::conditional_t, typename new_T_l::template concat, con::concat>; using r = typename type_name_impl::r; }; template struct type_name_impl { using new_T_l = std::conditional_t::l::size && !ptr_or_ref , spa::concat::l>, typename type_name_impl::l>; using l = std::conditional_t, typename new_T_l::template concat, vol::concat>; using r = typename type_name_impl::r; }; template struct type_name_impl { using new_T_l = std::conditional_t::l::size && !ptr_or_ref , spa::concat::l>, typename type_name_impl::l>; using l = std::conditional_t, typename new_T_l::template concat, con_vol::concat>; using r = typename type_name_impl::r; }; template struct type_name_impl { using l = std::conditional_t, typename type_name_impl ::l::template concat, typename type_name_impl ::l::template concat< ast>>; using r = std::conditional_t, rpa::concat::r>, typename type_name_impl::r>; }; template struct type_name_impl { using l = std::conditional_t, typename type_name_impl ::l::template concat, typename type_name_impl ::l::template concat< amp>>; using r = std::conditional_t, rpa::concat::r>, typename type_name_impl::r>; }; template struct type_name_impl { using l = std::conditional_t, typename type_name_impl ::l::template concat, typename type_name_impl ::l::template concat< amp, amp>>; using r = std::conditional_t, rpa::concat::r>, typename type_name_impl::r>; }; template struct type_name_impl { using l = std::conditional_t, typename type_name_impl ::l::template concat, nsp, ast>, typename type_name_impl::l::template concat< type_name_lit, nsp, ast>>; using r = std::conditional_t, rpa::concat::r>, typename type_name_impl::r>; }; template struct type_name_impl> { using l = typename type_name_impl::l; using r = lbr::concat::r>; }; template struct type_name_impl> { using l = typename type_name_impl::l; using r = lbr::concat, rbr, typename type_name_impl::r>; }; template struct type_name_impl { using l = typename type_name_impl::l; using r = lpa::concat::r>; }; template struct type_name_impl { using l = typename type_name_impl::l; using r = lpa::concat, com::concat>..., rpa, typename type_name_impl::r>; }; #define TYPE_NAME(t) template <> struct primitive_type_name {using value = STR_LIT(#t);};
#include #include 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 Type of s: PKc Real type of s: char const* one two three Type of i: i Real type of i: int