with macro language defines define c++ macros c-preprocessor

language - macro c++



¿Puede esta macro convertirse en una función? (16)

Como la respuesta de JohnMcG, pero

La desventaja es que tendrá una copia de esto en su binario para cada combinación de nombre de tipo, tamaño.

Es por eso que lo convertirías en una función de plantilla en línea .

Mientras refactorizo ​​el código y me libero de todos esos #defines que ahora nos enseñan a odiar, me encontré con esta belleza que se usa para calcular la cantidad de elementos en una estructura:

#define STRUCTSIZE(s) (sizeof(s) / sizeof(*s))

Muy útil como es, pero ¿se puede convertir en una función o plantilla en línea?

OK, ARRAYSIZE sería un nombre mejor, pero este es un código heredado (no tengo idea de dónde vino, tiene al menos 15 años) así que lo pegué ''como está''.


El tipo de una función de plantilla se deduce automáticamente, en contraste con el de una clase de plantilla. Puedes usarlo aún más simple:

template< typename T > size_t structsize( const T& t ) { return sizeof( t ) / sizeof( *t ); } int ints[] = { 1,2,3 }; assert( structsize( ints ) == 3 );

Pero estoy de acuerdo en que no funciona para las estructuras: funciona para las matrices. Así que prefiero llamarlo Arraysize :)


La macro tiene un nombre muy engañoso: la expresión en la macro devolverá el número de elementos en una matriz si el nombre de una matriz se pasa como el parámetro macro.

Para otros tipos obtendrá algo más o menos sin sentido si el tipo es un puntero o obtendrá un error de sintaxis.

Por lo general, esa macro se llama algo así como NUM_ELEMENTS () o algo así para indicar su verdadera utilidad. No es posible reemplazar la macro con una función en C, pero en C ++ se puede usar una plantilla.

La versión que uso se basa en el código del encabezado winnt.h de Microsoft (avíseme si publicar este fragmento va más allá del uso legítimo):

// // Return the number of elements in a statically sized array. // DWORD Buffer[100]; // RTL_NUMBER_OF(Buffer) == 100 // This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc. // #define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0])) #if defined(__cplusplus) && / !defined(MIDL_PASS) && / !defined(RC_INVOKED) && / !defined(_PREFAST_) && / (_MSC_FULL_VER >= 13009466) && / !defined(SORTPP_PASS) // // RtlpNumberOf is a function that takes a reference to an array of N Ts. // // typedef T array_of_T[N]; // typedef array_of_T &reference_to_array_of_T; // // RtlpNumberOf returns a pointer to an array of N chars. // We could return a reference instead of a pointer but older compilers do not accept that. // // typedef char array_of_char[N]; // typedef array_of_char *pointer_to_array_of_char; // // sizeof(array_of_char) == N // sizeof(*pointer_to_array_of_char) == N // // pointer_to_array_of_char RtlpNumberOf(reference_to_array_of_T); // // We never even call RtlpNumberOf, we just take the size of dereferencing its return type. // We do not even implement RtlpNumberOf, we just decare it. // // Attempts to pass pointers instead of arrays to this macro result in compile time errors. // That is the point. // extern "C++" // templates cannot be declared to have ''C'' linkage template <typename T, size_t N> char (*RtlpNumberOf( UNALIGNED T (&)[N] ))[N]; #define RTL_NUMBER_OF_V2(A) (sizeof(*RtlpNumberOf(A))) // // This does not work with: // // void Foo() // { // struct { int x; } y[2]; // RTL_NUMBER_OF_V2(y); // illegal use of anonymous local type in template instantiation // } // // You must instead do: // // struct Foo1 { int x; }; // // void Foo() // { // Foo1 y[2]; // RTL_NUMBER_OF_V2(y); // ok // } // // OR // // void Foo() // { // struct { int x; } y[2]; // RTL_NUMBER_OF_V1(y); // ok // } // // OR // // void Foo() // { // struct { int x; } y[2]; // _ARRAYSIZE(y); // ok // } // #else #define RTL_NUMBER_OF_V2(A) RTL_NUMBER_OF_V1(A) #endif #ifdef ENABLE_RTL_NUMBER_OF_V2 #define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V2(A) #else #define RTL_NUMBER_OF(A) RTL_NUMBER_OF_V1(A) #endif // // ARRAYSIZE is more readable version of RTL_NUMBER_OF_V2, and uses // it regardless of ENABLE_RTL_NUMBER_OF_V2 // // _ARRAYSIZE is a version useful for anonymous types // #define ARRAYSIZE(A) RTL_NUMBER_OF_V2(A) #define _ARRAYSIZE(A) RTL_NUMBER_OF_V1(A)

Además, el libro de Matthew Wilson "C ++ imperfecto" tiene un buen tratamiento de lo que está sucediendo aquí (Sección 14.3 - página 211-213 - Arrays and Pointers - dimensionof ()).


No creo que eso realmente resuelva la cantidad de elementos en una estructura. Si la estructura está empaquetada y usted usó elementos más pequeños que el tamaño del puntero (como por ejemplo, un carácter en un sistema de 32 bits), los resultados son incorrectos. Además, si la estructura contiene una estructura, ¡tú también estás equivocado!



Sí, se puede hacer una plantilla en C ++

template <typename T> size_t getTypeSize() { return sizeof(T)/sizeof(*T); }

usar:

struct JibbaJabba { int int1; float f; }; int main() { cout << "sizeof JibbaJabba is " << getTypeSize<JibbaJabba>() << std::endl; return 0; }

Vea la publicación de BCS arriba o abajo acerca de una buena manera de hacer esto con una clase en tiempo de compilación usando alguna metaprogramación de plantillas ligeras.


Simplfying @ KTC''s, ya que tenemos el tamaño de la matriz en el argumento de la plantilla:

template<typename T, int SIZE> int arraySize(const T(&arr)[SIZE]) { return SIZE; }

La desventaja es que tendrá una copia de esto en su binario para cada combinación de nombre de tipo, tamaño.


Su macro está mal llamada, debería llamarse ARRAYSIZE. Se usa para determinar el número de elementos en una matriz cuyo tamaño se fija en el momento de la compilación. Esta es una forma en que puede funcionar:

char foo [128]; // En realidad, tendrías alguna expresión constante o constante como el tamaño de la matriz.

para (sin signo i = 0; i <STRUCTSIZE (foo); ++ i) {}

Es un poco frágil de usar, porque puedes cometer este error:

char * foo = nuevo carácter [128];

para (sin signo i = 0; i <STRUCTSIZE (foo); ++ i) {}

Ahora iterarás para i = 0 a <1 y te arrancarás el pelo.


xtofl tiene la respuesta correcta para encontrar un tamaño de matriz. No se necesita ninguna macro o plantilla para encontrar el tamaño de una estructura, ya que sizeof () debería funcionar bien.

Estoy de acuerdo en que el preprocesador es malo , pero hay ocasiones en que es la menos malvada de las alternativas .


  • función, sin función de plantilla, sí
  • plantilla, creo que sí (pero C ++
  • las plantillas no son lo mío)

Editar: del código de Doug

template <typename T> uint32_t StructSize() // This might get inlined to a constant at compile time { return sizeof(T)/sizeof(*T); } // or to get it at compile time for shure class StructSize<typename T> { enum { result = sizeof(T)/sizeof(*T) }; }

Me han dicho que el segundo no funciona. OTOH algo así debería ser viable, simplemente no uso C ++ suficiente para arreglarlo.

Una página en C ++ (y D) plantillas para compilar cosas de tiempo



Ninguna hasta ahora ha propuesto una forma portátil de obtener el tamaño de una matriz cuando solo tienes una instancia de una matriz y no su tipo. (typeof y _countof no son portátiles, por lo que no se pueden usar).

Lo haría de la siguiente manera:

template<int n> struct char_array_wrapper{ char result[n]; }; template<typename T, int s> char_array_wrapper<s> the_type_of_the_variable_is_not_an_array(const T (&array)[s]){ } #define ARRAYSIZE_OF_VAR(v) sizeof(the_type_of_the_variable_is_not_an_array(v).result) #include <iostream> using namespace std; int main(){ int foo[42]; int*bar; cout<<ARRAYSIZE_OF_VAR(foo)<<endl; // cout<<ARRAYSIZE_OF_VAR(bar)<<endl; fails }

  • Funciona cuando solo el valor está alrededor.
  • Es portátil y solo usa std-C ++.
  • Falla con un mensaje de error descriptiv.
  • No evalúa el valor. (No puedo pensar en una situación en la que esto sería un problema porque una función no puede devolver el tipo de matriz, pero es mejor que lo lamente).
  • Devuelve el tamaño como constante de compilación.

Envolví el constructo en una macro para tener una sintaxis decente. Si quiere deshacerse de él, su única opción es hacer la sustitución manualmente.


Para las matrices de longitud variable de estilo C99, parece que el enfoque macro puro (sizeof (arr) / sizeof (arr [0])) es el único que funcionará.


Como se ha dicho, el código calcula la cantidad de elementos en una matriz, no en una estructura. Simplemente escribiría la división sizeof () explícitamente cuando lo desee. Si tuviera que hacer una función, me gustaría dejar en claro en su definición que está esperando una matriz.

template<typename T,int SIZE> inline size_t array_size(const T (&array)[SIZE]) { return SIZE; }

Lo anterior es similar al de xtofl , excepto que protege contra pasar un puntero a él (que dice apuntar a una matriz dinámicamente asignada) y obtener la respuesta incorrecta por error.

EDITAR : Simplificado según JohnMcG . EDITAR : en línea

Desafortunadamente, lo anterior no proporciona una respuesta en tiempo de compilación (incluso si el compilador lo hace en línea y lo optimiza para que sea una constante debajo del capó), por lo que no puede usarse como una expresión constante de tiempo de compilación. es decir, no se puede usar como tamaño para declarar una matriz estática. En C ++ 0x, este problema desaparece si se reemplaza la palabra clave en línea por constexpr (constexpr está en línea implícitamente).

constexpr size_t array_size(const T (&array)[SIZE])

La solución de jwfearn funciona para el tiempo de compilación, pero implica tener un typedef que efectivamente "guardó" el tamaño de la matriz en la declaración de un nuevo nombre. El tamaño de la matriz se resuelve inicializando una constante a través de ese nuevo nombre. En tal caso, uno también puede simplemente guardar el tamaño de la matriz en una constante desde el principio.

La solución publicada de Martin York también funciona en tiempo de compilación, pero implica el uso del operador typeof () no estándar. El trabajo para eso es esperar C ++ 0x y usar decltype (en ese momento uno no lo necesitaría para este problema ya que tendremos constexpr ). Otra alternativa es usar Boost.Typeof, en cuyo caso terminaremos con

#include <boost/typeof/typeof.hpp> template<typename T> struct ArraySize { private: static T x; public: enum { size = sizeof(T)/sizeof(*x)}; }; template<typename T> struct ArraySize<T*> {};

y se usa por escrito

ArraySize<BOOST_TYPEOF(foo)>::size

donde foo es el nombre de una matriz.


La solución de KTC es limpia, pero no se puede usar en tiempo de compilación y depende de la optimización del compilador para evitar la saturación de código y la sobrecarga de llamadas a la función.

Se puede calcular el tamaño de la matriz con una metafunción de solo tiempo de compilación con cero costo de tiempo de ejecución. BCS estaba en el camino correcto, pero esa solución es incorrecta.

Aquí está mi solución:

// asize.hpp template < typename T > struct asize; // no implementation for all types... template < typename T, size_t N > struct asize< T[N] > { // ...except arrays static const size_t val = N; }; template< size_t N > struct count_type { char val[N]; }; template< typename T, size_t N > count_type< N > count( const T (&)[N] ) {} #define ASIZE( a ) ( sizeof( count( a ).val ) ) #define ASIZET( A ) ( asize< A >::val )

con código de prueba (usando Boost.StaticAssert para demostrar el uso en tiempo de compilación solo):

// asize_test.cpp #include <boost/static_assert.hpp> #include "asize.hpp" #define OLD_ASIZE( a ) ( sizeof( a ) / sizeof( *a ) ) typedef char C; typedef struct { int i; double d; } S; typedef C A[42]; typedef S B[42]; typedef C * PA; typedef S * PB; int main() { A a; B b; PA pa; PB pb; BOOST_STATIC_ASSERT( ASIZET( A ) == 42 ); BOOST_STATIC_ASSERT( ASIZET( B ) == 42 ); BOOST_STATIC_ASSERT( ASIZET( A ) == OLD_ASIZE( a ) ); BOOST_STATIC_ASSERT( ASIZET( B ) == OLD_ASIZE( b ) ); BOOST_STATIC_ASSERT( ASIZE( a ) == OLD_ASIZE( a ) ); BOOST_STATIC_ASSERT( ASIZE( b ) == OLD_ASIZE( b ) ); BOOST_STATIC_ASSERT( OLD_ASIZE( pa ) != 42 ); // logic error: pointer accepted BOOST_STATIC_ASSERT( OLD_ASIZE( pb ) != 42 ); // logic error: pointer accepted // BOOST_STATIC_ASSERT( ASIZE( pa ) != 42 ); // compile error: pointer rejected // BOOST_STATIC_ASSERT( ASIZE( pb ) != 42 ); // compile error: pointer rejected return 0; }

Esta solución rechaza los tipos que no son de matriz en tiempo de compilación, por lo que no se confundirán con los punteros como lo hace la versión de macro.


Prefiero el método enum sugerido por [BCS] (en ¿Se puede convertir esta macro en una función? )

Esto se debe a que puede usarlo cuando el compilador espera una constante de tiempo de compilación. La versión actual del idioma no le permite usar los resultados de las funciones para los tiempos de compilación, pero creo que esto vendrá en la próxima versión del compilador:

El problema con este método es que no genera un error de tiempo de compilación cuando se utiliza con una clase que ha sobrecargado el operador ''*'' (consulte el código a continuación para obtener más información).

Desafortunadamente, la versión suministrada por ''BCS'' no se compila como se esperaba, así que aquí está mi versión:

#include <iterator> #include <algorithm> #include <iostream> template<typename T> struct StructSize { private: static T x; public: enum { size = sizeof(T)/sizeof(*x)}; }; template<typename T> struct StructSize<T*> { /* Can only guarantee 1 item (maybe we should even disallow this situation) */ //public: enum { size = 1}; }; struct X { int operator *(); }; int main(int argc,char* argv[]) { int data[] = {1,2,3,4,5,6,7,8}; int copy[ StructSize<typeof(data)>::size]; std::copy(&data[0],&data[StructSize<typeof(data)>::size],&copy[0]); std::copy(&copy[0],&copy[StructSize<typeof(copy)>::size],std::ostream_iterator<int>(std::cout,",")); /* * For extra points we should make the following cause the compiler to generate an error message */ X bad1; X bad2[StructSize<typeof(bad1)>::size]; }