Аргументы шаблона Stringifying

Названные объемы технически в порядке здесь, это просто, они не используются таким образом очень часто. Поэтому, когда кто-то еще приезжает для поддержания кода в будущем, не может быть сразу очевидно, почему они там. По моему скромному мнению, частный вспомогательный метод был бы лучшим выбором...

34
задан fish2000 5 August 2016 в 16:06
поделиться

5 ответов

Вы можете использовать некоторую магию шаблонов.

#include <iostream>

template <typename T>
struct TypeName { static const char *name; };

template <typename T>
const char *TypeName<T>::name = "unknown";

template <>
const char *TypeName<int>::name = "int";

template <typename T>
struct Stringify
{
     Stringify()
     {
          std::cout << TypeName<T>::name << std::endl;
     }
};

int main() 
{
     Stringify<int> s;
}

Это имеет преимущество перед RTTI (например, typeinfo ) - это разрешается во время компиляции; и недостаток - вам нужно предоставить информацию о типе самостоятельно (если нет какой-то библиотеки, которая уже делает это, о которой я не знаю; может быть, что-то даже в Boost).

Или, как Matrin York предложила в комментариях используйте вместо этого встроенные шаблоны функций:

template <typename T>
inline const char* typeName(void) { return "unknown"; }

template <>
inline const char* typeName<int>(void) { return "int"; }

// ...
std::cout << typeName<T>() << std::endl;

Но, если вам когда-нибудь понадобится хранить больше информации об этом конкретном типе, тогда шаблоны классов, вероятно, будут лучше.

23
ответ дан 27 November 2019 в 16:14
поделиться

You could try

 typeid(T).name()

Edit: Fixed based on comments.

34
ответ дан 27 November 2019 в 16:14
поделиться

No, you cannot work on types as if they were variables. You could write code that extracted the typeid() of an element and printed the name, but the resulting value will probably not be what you expect (type names are not standarized).

You can also work with template specializations (and some macro magic) to achieve a more interesting version if the number of types you want to work with is limited:

template <typename T> const char* printtype(); // not implemented

// implement specializations for given types
#define DEFINE_PRINT_TYPE( type ) \
template<>\
const char* printtype<type>() {\
   return #type;\
}
DEFINE_PRINT_TYPE( int );
DEFINE_PRINT_TYPE( double );
// ... and so on
#undef DEFINE_PRINT_TYPE
template <typename T> void test()
{
   std::cout << printtype<T>() << std::endl;
}
int main() {
   test<int>();
   test<double>();
   test<float>(); // compilation error, printtype undefined for float
}

Or you could even combine both versions: implement the printtype generic template using typeinfo and then provide specializations for the types you want to have fancier names.

template <typename T>
const char* printtype()
{
   return typeid(T).name();
}
12
ответ дан 27 November 2019 в 16:14
поделиться

This breaks one of my primary tenets of C++ code writing: Avoid using tricks in both the template features and the preprocessor at the same time.

Part of the reason for templates and the nastiness they introduce into the language was an attempt to wean developers away from using the preprocessor. If you use both, then the terrorists win.

4
ответ дан 27 November 2019 в 16:14
поделиться

Ваш код не работает, потому что препроцессор, отвечающий за поиск и расширение макросов, которые вы используете в вашем коде, не знает самого языка. Это просто парсер текста. Он находит STRINGIFY (T) в самом шаблоне функции и расширяет его задолго до того, как вы зададите тип этому шаблону. Как оказалось, к сожалению, вы всегда будете получать «T» вместо ожидаемого имени.

Как и предполагал litb , я (плохо) реализовал этот шаблон функции `getTypeName ', который возвращает typename, которое вы передаете:

#include <iostream>

template <typename _Get_TypeName>
const std::string &getTypeName()
{
    static std::string name;

    if (name.empty())
    {
        const char *beginStr = "_Get_TypeName =";
        const size_t beginStrLen = 15; // Yes, I know...
                                       // But isn't it better than strlen()?

        size_t begin,length;
        name = __PRETTY_FUNCTION__;

        begin = name.find(beginStr) + beginStrLen + 1;
        length = name.find("]",begin) - begin;
        name = name.substr(begin,length);
    }

    return name;
}

int main()
{
    typedef void (*T)(int,int);

    // Using getTypeName()
    std::cout << getTypeName<float>() << '\n';
    std::cout << getTypeName<T>() << '\n'; // You don't actually need the
                                           // typedef in this case, but
                                           // for it to work with the
                                           // typeid below, you'll need it

    // Using typeid().name()
    std::cout << typeid(float).name() << '\n';
    std::cout << typeid(T).name() << '\n';

    return 0;
}

Приведенный выше код приводит к следующему результату с включенным флагом GCC -s («удаление всех символов из двоичного кода»):

float
void (*)(int, int)
f
PFviiE

Итак, как видите, getTypename () работает лучше, при стоимость этого уродливого хака для синтаксического анализа строк (Я ЗНАЮ, это чертовски уродливо).

Несколько моментов, которые следует принять во внимание:

  • Код предназначен только для GCC. Я не знаю, как его перенести на другой компилятор. Вероятно, только у некоторых других есть такая возможность для создания таких красивых имен функций, и, судя по тому, что я искал, MSVC ++ не имеет такой возможности, если вы спрашиваете себя.
  • Если в новой версии GCC форматирует __ PRETTY_FUNCTION __ иначе, сопоставление строк может нарушиться, и вам придется это исправить. По этой же причине я также предупреждаю, что getTypeName () может быть хорошим для отладки (и, тем не менее, может даже не подходить для этого), но это определенно плохо, плохо и плохо для других целей, таких как сравнение двух типов в шаблоне или что-то в этом роде (я не знаю, просто догадываюсь, что кто-то может подумать ...). Используйте его исключительно для отладки и предпочтительно не t вызывать его в сборках выпуска (использовать макросы для отключения), чтобы вы не использовали __ PRETTY_FUNCTION __ и, таким образом, компилятор не выдает для него строку.
  • Я определенно не эксперт, и я не уверен, может ли какой-то странный тип привести к сбою сопоставления строк. Я хотел бы попросить людей, которые читают этот пост, прокомментировать, знают ли они о таком случае.
  • В коде используется статический std :: string. Это означает, что, если какое-то исключение выбрасывается из его конструктора или деструктора, оно никак не дойдет до блока catch, и вы получите необработанное исключение. Я не знаю, могут ли std :: strings это сделать, но имейте в виду, что если они это сделают, у вас могут быть проблемы. Я использовал его, потому что ему нужен деструктор для освобождения памяти. Вы можете реализовать для этого свой собственный класс, обеспечение того, чтобы исключение не генерировалось, кроме сбоя выделения (это в значительной степени фатально, не так ли? Итак ...), и возвращать простую C-строку.
  • С помощью typedefs вы можете получить некоторые странные результаты, подобные этому (для некоторых причина, сайт нарушает форматирование этого фрагмента, поэтому я использую эту ссылку для вставки): http://pastebin.com/f51b888ad

Несмотря на эти недостатки, я хотел бы сказать, что это точно быстрый. Во второй раз, когда вы ищите одно и то же имя типа, потребуется выбрать ссылку на глобальный std :: string, содержащий это имя. И, по сравнению с методами специализации шаблонов, предложенными ранее, вам больше не нужно объявлять ничего, кроме самого шаблона, поэтому его действительно намного проще использовать.

  • С typedefs вы можете получить некоторые странные результаты, подобные этому (по какой-то причине сайт нарушает форматирование этого фрагмента, поэтому я использую эту ссылку для вставки): http://pastebin.com/f51b888ad
  • Несмотря на эти недостатки, я хотел бы сказать, что он работает быстро. Во второй раз, когда вы ищете одно и то же имя типа, потребуется выбрать ссылку на глобальный std :: string, содержащий это имя. И, по сравнению с методами специализации шаблонов, предложенными ранее, вам больше не нужно объявлять ничего, кроме самого шаблона, поэтому его действительно намного проще использовать.

  • С typedefs вы можете получить некоторые странные результаты, подобные этому (по какой-то причине сайт нарушает форматирование этого фрагмента, поэтому я использую эту ссылку для вставки): http://pastebin.com/f51b888ad
  • Несмотря на эти недостатки, я хотел бы сказать, что он работает быстро. Во второй раз, когда вы ищите одно и то же имя типа, потребуется выбрать ссылку на глобальный std :: string, содержащий это имя. И, по сравнению с методами специализации шаблонов, предложенными ранее, вам больше не нужно объявлять ничего, кроме самого шаблона, поэтому его действительно намного проще использовать.

    Хочу сказать, что это очень быстро. Во второй раз, когда вы ищите одно и то же имя типа, потребуется выбрать ссылку на глобальный std :: string, содержащий это имя. И, по сравнению с методами специализации шаблонов, предложенными ранее, вам больше не нужно объявлять ничего, кроме самого шаблона, поэтому его действительно намного проще использовать.

    Хочу сказать, что это очень быстро. Во второй раз, когда вы ищите одно и то же имя типа, потребуется выбрать ссылку на глобальный std :: string, содержащий это имя. И, по сравнению с методами специализации шаблонов, предложенными ранее, вам больше не нужно объявлять ничего, кроме самого шаблона, поэтому его действительно намного проще использовать.

    16
    ответ дан 27 November 2019 в 16:14
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: