Распечатать имя шаблона во время компиляции
При создании функции шаблона в C++ есть простой способ, чтобы имя типа шаблона было представлено в виде строки? У меня есть простой тестовый пример, чтобы показать, что я пытаюсь сделать (обратите внимание, что указанный код не компилируется):
#include <stdio.h>
template <typename type>
type print(type *addr)
{
printf("type is: %s",typename);
}
int main()
{
int a;
print(&a);
}
// Would like to print something like:
// type is: int
Я думаю, что typename должно быть доступно во время компиляции, когда создается экземпляр функции, но я не очень знаком с шаблонами и не видел способа получить typename в виде строки.
Причина, по которой я хочу сделать это для некоторой отладки типа printf. У меня есть несколько потоков, работающих и GDB меняет поведение программы. Поэтому для некоторых вещей я хочу получить информацию о том, какие функции выполнялись. Это не слишком важно, поэтому, если решение слишком сложное, я бы пропустил добавление этой информации в свою функцию регистрации. Но если бы существовал простой способ сделать это, была бы полезна информация.
9 ответов
__PRETTY_FUNCTION__
должен решить вашу проблему (по крайней мере, во время выполнения)
Вывод программы ниже:
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf test<type>::test() [with type = int]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
asfdasdfasdf void tempFunction() [with type = bool]
!!!Hello World!!!
Если вам действительно нужно имя типа в виде строки, вы можете взломать это (используя snprintf
вместо printf
) и вытяните подстроку после '=' и до ']'.
#include <iostream>
using namespace std;
template<typename type>
class test
{
public:
test()
{
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
}
};
template<typename type>
void tempFunction()
{
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
printf("asfdasdfasdf %s\n", __PRETTY_FUNCTION__);
}
int main() {
test<int> test;
tempFunction<bool>();
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
return 0;
}
Чтобы получить полезное имя времени компиляции:
Предположим, у вас есть какой-то неизвестный тип с именем "T". Вы можете заставить компилятор печатать его тип, используя его ужасно. Например:
typedef typename T::something_made_up X;
Сообщение об ошибке будет выглядеть так:
error: no type named 'something_made_up' in 'Wt::Dbo::ptr<trader::model::Candle>'
Бит после 'in' показывает тип. (Проверено только с лязгом).
Другие способы его запуска:
bool x = T::nothing; // error: no member named 'nothing' in 'Wt::Dbo::ptr<trader::model::Candle>'
using X = typename T::nothing; // error: no type named 'nothing' in 'Wt::Dbo::ptr<trader::model::Candle>'
С C++11 у вас уже может быть объект и вы можете использовать 'decltype', чтобы получить его тип, так что вы также можете запустить:
auto obj = creatSomeObject();
bool x = decltype(obj)::nothing; // (Where nothing is not a real member).
Другое решение времени компиляции, похожее на то, которое предоставляет matiu, но, возможно, немного более наглядное, заключалось бы в использовании static_assert
завернутый в небольшую вспомогательную функцию.
template<typename T>
void print_type_in_compilation_error(T&&)
{
static_assert(std::is_same<T, int>::value && !std::is_same<T, int>::value, "Compilation failed because you wanted to read the type. See below");
}
// usage:
int I;
print_type_in_compilation_error(I);
Выше приведено хорошее сообщение об ошибке (проверено в MSVC и Clang), как и в другом ответе, но код ИМХО лучше понять.
Поскольку вы сказали, что вам это понадобится для целей отладки, возможно, также приемлемо решение во время выполнения. И вы пометили это как g++, поэтому вы не хотите соответствовать стандарту.
Вот что это значит:
#include <cxxabi.h> // the libstdc++ used by g++ does contain this header
template <typename type>
void print(const type *addr) // you wanted a pointer
{
char * name = abi::__cxa_demangle(typeid(*addr).name(), 0, 0, NULL);
printf("type is: %s\n", name);
free(name);
}
print(new unsigned long); // prints "type is: unsigned long"
print(new std::vector<int>); // prints "type is: std::vector<int, std::allocator<int> >"
РЕДАКТИРОВАТЬ: исправлена утечка памяти. Спасибо Джесси.
Есть библиотека Boost.TypeIndex.
Смотрите boost::typeindex::type_id для подробностей.
Это очень простое в использовании, кроссплатформенное и реальное решение типа компиляции. Также это работает, даже если нет RTTI. Также большинство компиляторов поддерживаются из коробки.
Вы можете использовать С++ 20
concept
s для определения ограничения, которому не может удовлетворить ни один тип. Использование этого ограничения для аргумента шаблона и создание экземпляра шаблона приведет к сбою компилятора и выводу выведенного имени типа.
void print_typename (auto) requires false {}
// Equivalent to
// template<typename T>
// concept unsatisfiable_c = false;
//
// template<unsatisfiable_c T>
// void print_typename (T) {}
int main (int, char**) {
int foo = 73;
print_typename(foo);
// error: no matching function for call to 'print_typename'
// note: candidate template ignored: constraints not satisfied [with auto:1 = int]
// ^^^
return 0;
}
Если твой
T
более сложный, например
T=std::vector<std::pair<key_t, value_t>>
или же
T=void (*)(U, V const&)
, и вы хотите конкретно узнать
key_t
или же
V
, вы можете сделать это следующим образом:
template<typename T>
concept unsatisfiable_c = false;
template<unsatisfiable_c key_t, typename value_t>
void print_key_t (std::vector<std::pair<key_t, value_t>>) {}
template<typename U, unsatisfiable_c V>
void print_V (void (*)(U, V const&)) {}
void foo (double, long double const&) {}
int main (int, char**) {
std::vector<std::pair<long, char>> bar;
print_key_t(bar);
// error: no matching function for call to 'print_key_t'
// note: candidate template ignored: constraints not satisfied [with key_t = long, value_t = char]
print_V(&foo);
// error: no matching function for call to 'print_V'
// note: candidate template ignored: constraints not satisfied [with U = double, V = long double]
return 0;
}
Как прокомментировали @chris и @Philipp, если вам действительно не нужно имя во время компиляции, вы можете использовать typeid(type).name()
после включения <typeinfo>
,
если вам все равно, оператор нарушит компиляцию, самый простой способ:
static_assert( 1== T::not_existed_member_name, "print the T type when compile" )
нет возможности напечатать тип, не нарушая компиляцию. (конечно, здесь вы можете вызвать предупреждение, а не ошибку)
Если у вас есть известный набор используемых типов для создания экземпляра шаблона, мы можем использовать подход, описанный в этом старом потоке: /questions/19440402/c-poluchit-imya-tipa-v-shablone