Saltar al contenido

¿Es posible imprimir el tipo de una variable en C ++ estándar?

Solución:

Actualización de C ++ 11 a una pregunta muy antigua: Imprimir tipo de variable en C ++.

La respuesta aceptada (y buena) es usar typeid(a).name(), dónde a es un nombre de variable.

Ahora en C ++ 11 tenemos decltype(x), que puede convertir una expresión en un tipo. Y decltype() viene con su propio conjunto de reglas muy interesantes. Por ejemplo decltype(a) y decltype((a)) generalmente serán de diferentes tipos (y por buenas y comprensibles razones una vez que se expongan esas razones).

¿Será nuestro fiel typeid(a).name() ayúdanos a explorar este nuevo mundo feliz?

No.

Pero la herramienta que lo hará no es tan complicada. Y es esa herramienta la que estoy usando como respuesta a esta pregunta. Compararé y contrastaré esta nueva herramienta con typeid(a).name(). Y esta nueva herramienta está construida sobre typeid(a).name().

El problema fundamental:

typeid(a).name()

descarta calificadores de cv, referencias y lvalue / rvalue-ness. Por ejemplo:

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

Para mí salidas:

i

y supongo que en las salidas de MSVC:

int

Es decir, el const se ha ido. Este no es un problema de QOI (Calidad de implementación). El estándar exige este comportamiento.

Lo que recomiendo a continuación es:

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

que se usaría así:

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

y para mi salidas:

int const

<disclaimer> No he probado esto en MSVC. </disclaimer> Pero agradezco los comentarios de quienes lo hacen.

La solución C ++ 11

estoy usando __cxa_demangle para plataformas que no son MSVC como lo recomienda ipapadop en su respuesta a los tipos de demanda. Pero en MSVC confío typeid para exigir nombres (no probado). Y este núcleo está envuelto en algunas pruebas simples que detectan, restauran e informan calificadores de cv y referencias al tipo de entrada.

#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;
}

Los resultados

Con esta solución puedo hacer esto:

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';
}

y la salida es:

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

Tenga en cuenta (por ejemplo) la diferencia entre decltype(i) y decltype((i)). El primero es el tipo de declaración de i. Este último es el “tipo” de expresión i. (las expresiones nunca tienen un tipo de referencia, sino como una convención decltype representa expresiones lvalue con referencias lvalue).

Por lo tanto, esta herramienta es un vehículo excelente para aprender decltype, además de explorar y depurar su propio código.

Por el contrario, si tuviera que construir esto solo en typeid(a).name(), sin volver a agregar calificadores o referencias de cv perdidos, el resultado sería:

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

Es decir, se eliminan todas las referencias y calificadores cv.

Actualización de C ++ 14

Justo cuando crees que tienes una solución a un problema, siempre aparece alguien de la nada y te muestra un camino mucho mejor. 🙂

Esta respuesta de Jamboree muestra cómo obtener el nombre del tipo en C ++ 14 en tiempo de compilación. Es una solución brillante por un par de razones:

  1. ¡Está en tiempo de compilación!
  2. Obtienes el propio compilador para hacer el trabajo en lugar de una biblioteca (incluso una std :: lib). Esto significa resultados más precisos para las últimas funciones del lenguaje (como lambdas).

La respuesta de Jamboree no presenta todo para VS, y estoy modificando un poco su código. Pero dado que esta respuesta recibe muchas visitas, tómese un tiempo para ir allí y votar su respuesta, sin la cual, esta actualización nunca habría sucedido.

#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
}

Este código retrocederá automáticamente en el constexpr si todavía está atascado en el antiguo C ++ 11. Y si está pintando en la pared de la cueva con C ++ 98/03, el noexcept también se sacrifica.

Actualización de C ++ 17

En los comentarios a continuación, Lyberta señala que el nuevo std::string_view puede sustituir 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
}

Actualicé las constantes para VS gracias al muy buen trabajo de detective de Jive Dadson en los comentarios a continuación.

Actualizar:

Asegúrese de revisar esta reescritura a continuación que elimina los números mágicos ilegibles en mi última formulación.

Tratar:

#include <typeinfo>

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

Es posible que deba activar RTTI en las opciones de su compilador para que esto funcione. Además, el resultado de esto depende del compilador. Puede ser un nombre de tipo sin formato o un símbolo de alteración de nombres o cualquier cosa intermedia.

Muy feo, pero funciona si solo desea información de tiempo de compilación (por ejemplo, para depuración):

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

Devoluciones:

Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:5:19: error: 'foo' is not a member of 'std::tuple<int, double, const char*>'
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *